服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#设计模式之Singleton模式

C#设计模式之Singleton模式

2022-09-14 15:16老胡写代码 C#

这篇文章主要介绍了C#设计模式中的Singleton模式相关知识,文中代码非常详细,供大家理解学习,感兴趣的朋友可以了解下

前言

singleton是二十三个设计模式中比较重要也比较经常使用的模式。但是这个模式虽然简单,实现起来也会有一些小坑,让我们一起来看看吧!

实现思路

首先我们看看这个设计模式的uml类图。

C#设计模式之Singleton模式

很清晰的可以看到,有三点是需要我们在实现这个模式的时候注意的地方。

  • 私有化的构造器
  • 全局唯一的静态实例
  • 能够返回全局唯一静态实例的静态方法

其中,私有化构造器是防止外部用户创建新的实例而静态方法用于返回全局唯一的静态实例供用户使用。原理清楚了,接下来我们看看一些典型的实现方式和其中的暗坑。

实现方法

最简单的实现方法

最简单的实现方法自然就是按照uml类图直接写一个类,我们看看代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class program
{
 static void main(string[] args)
 {
   var single1 = singleton.instance;
  var single2 = singleton.instance;
  console.writeline(object.referenceequals(single1, single2));
  console.readline();
 }
}
 
class singleton
{
 private static singleton _instance = null;
 private singleton()
 {
  console.writeline("created");
 }
 
 public static singleton instance
 {
  get
  {
   if (_instance == null)
   {
    _instance = new singleton();
   }
   return _instance;
  }
 }
 
 public void dumbmethod()
 {
 
 }
}

这段代码忠实的实现了uml类图里面的一切,查看输出结果,

C#设计模式之Singleton模式

证实了singleton确实起了作用,多次调用仅仅产生了一个实例,似乎这么写就可以实现这个模式了。但是,真的会那么简单吗?

如果多线程乱入?

现在我们给刚刚的例子加点调料,假设多个对实例的调用,并不是简单的,彬彬有礼的顺序关系,二是以多线程的方式调用,那么刚刚那种实现方法,还能从容应对吗?让我们试试。把main函数里面的调用改成这样。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  static void main(string[] args)
{
 int total = 10000;
 task[] tasks = new task[total];
 for (int i = 0; i < total; i++)
 {
  tasks[i] = task.factory.startnew(() =>
  {
   singleton.instance.dumbmethod();
  });
 }
          task.waitall(tasks);
 console.readline();
}

通过factory创造出1万个task,几乎同时去请求这个单例,看看输出。

C#设计模式之Singleton模式

咦,我们刚刚写的singleton模式失效了,这个类被创造了5次(这段代码运行多次,这个数字不一定相同),一定是多线程搞的鬼,我们刚刚写的代码没有办法应对多线程,换句话说,是非线程安全的(thread-safe),那有没有办法来攻克这个难关呢?

线程安全的单例模式

lock版本

提到线程安全,很多同学第一反应就是用lock,不错,lock是个可行的办法,让我们试试。添加一个引用类型的对象作为lock对象,修改代码如下(什么?你问我为什必须是引用类型的对象而不能是值类型的对象?因为lock的时候,如果对象是值类型,那么lock仅仅锁住了它的一个副本,另外一个线程可以畅通无阻的再次lock,这样lock就失去了阻塞线程的意义)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  private static object _syncobj = new object();
public static singleton instance
{
 get
 {
  lock (_syncobj)
  {
   if (_instance == null)
   {
    _instance = new singleton();
   }
   return _instance;
  }   
 }
}

运行一下,输出

C#设计模式之Singleton模式

只有一个实例创建,证明lock起作用了,这个模式可行!不过有些不喜欢用lock的同学可能要问,还有没有其他办法呢?答案是有的。

静态构造器版本

回想一下,c#中的类静态构造器,只会在这个类第一次被使用的时候调用一次,天然的线程安全,那我们试试不用lock使用类静态构造器?修改singleton类如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class singleton
{
 private static singleton _instance = null;
 private singleton()
 {
  console.writeline("created");
 }
 
 static singleton()
 {
  _instance = new singleton();
 }
 
 //private static object _syncobj = new object();
 public static singleton instance
 {
  get { return _instance; }
 }
 
 public void dumbmethod()
 {
 
 }
}

去掉了lock,添加了一个类静态构造器,试一试。

C#设计模式之Singleton模式

完美!对于不喜欢用lock(在这个例子中,实例只会创建一次但是之后的所有线程都要先排队lock再进入critical code进行检查,效率比较低下)的同学,类静态构造器提供了一种很好的选择。
不过俗话说,人心苦不足 , 我们总是追求卓越。这个版本比lock版本似乎更好一点,那还有没有更好的版本呢?有的。

lazy版本

从net 4.0开始,c#开始支持延迟初始化,通过lazy关键字,我们可以声明某个对象为仅仅当第一次使用的时候,再初始化,如果一直没有调用,那就不初始化,省去了一部分不必要的开销,提升了效率。如果你不熟悉lazy或者想更多了解它,请。我们今天关注的重点在于,lazy也是天生线程安全的,所以我们尝试用它来实现singleton模式?修改代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class singleton
{
 private static lazy<singleton> _instance = new lazy<singleton>(() => new singleton());
 private singleton()
 {
  console.writeline("created");
 }
 
 public static singleton instance
 {
  get
  {
   return _instance.value;
  }
 }
 
 public void dumbmethod()
 {
 
 }
}

输出结果中可以看到,我们达到了想要的效果:

C#设计模式之Singleton模式

在上面的代码中,私有变量_instance现在是被声明为延迟初始化,这样不但天然实现了线程安全,同时在没有调用instance静态方法的时候(也即没有调用_instance.value),初始化不会发生,这样就提高了效率。

总结

singleton模式很常见,实现起来也很简单,只是要小心线程安全。以上三种方法都可以实现线程安全的singleton模式。如果net 版本在4.0之上,建议使用lazy版本,毕竟对比lock版本,lazy版本可以免去实现手动lock之苦,对比static版本,又有延迟初始化的性能优势,何乐而不为呢?

以上就是c#设计模式之singleton模式的详细内容,更多关于c#中的singleton模式的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/deatharthas/p/13047660.html

延伸 · 阅读

精彩推荐
  • C#C#中OpenCvSharp 通过特征点匹配图片的方法

    C#中OpenCvSharp 通过特征点匹配图片的方法

    这篇文章主要介绍了OpenCvSharp 通过特征点匹配图片的方法,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...

    StupidsCat7332022-08-05
  • C#ASP.NET总结C#中7种获取当前路径的方法

    ASP.NET总结C#中7种获取当前路径的方法

    本文主要介绍了7种获取当前路径的方法,并做了代码演示,分享给大家,感兴趣的朋友可以参考一下。...

    Masonlu4282021-11-16
  • C#C#使用Process类调用外部exe程序

    C#使用Process类调用外部exe程序

    本文通过两个示例讲解了一下Process类调用外部应用程序的基本用法,并简单讲解了StartInfo属性,有需要的朋友可以参考一下。...

    一恋乘魔5352021-11-17
  • C#WinForm绘制圆角的方法

    WinForm绘制圆角的方法

    这篇文章主要介绍了WinForm绘制圆角的方法,涉及C#中GraphicsPath模块的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    我心依旧10382021-10-20
  • C#详解如何获取C#类中发生数据变化的属性信息

    详解如何获取C#类中发生数据变化的属性信息

    这篇文章主要介绍了详解如何获取C#类中发生数据变化的属性信息,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    墨墨墨墨小宇5802022-09-08
  • C#C# SESSION丢失问题的解决办法

    C# SESSION丢失问题的解决办法

    这篇文章主要为大家详细介绍了C# SESSION丢失问题的解决办法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    淡泊明志_宁静致远9162021-12-30
  • C#C#实现的字符串转MD5码函数实例

    C#实现的字符串转MD5码函数实例

    这篇文章主要介绍了C#实现的字符串转MD5码函数,结合简单实例形式分析了C#字符串的转换、遍历、加密等操作技巧,需要的朋友可以参考下...

    kagula6922021-11-30
  • C#C# 用什么方法将BitConverter.ToString产生字符串再转换回去

    C# 用什么方法将BitConverter.ToString产生字符串再转换回去

    这篇文章主要介绍了C# 用什么方法将BitConverter.ToString产生字符串再转换回去,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学...

    森大科技4012022-08-20