都知道singleton模式的线程安全方式有两种,一种是在GetInstance方法定义上加上synchronized的锁. 另一种方式是申明成static类型的变量.
我一直有一个疑问.net如何保证static类型是线程安全(实例化变量的操作时间很长,如何保证线程安全)? 我做了如下实验,的确是线程安全的.
[Form1.cs文件(对话框上仅一个按钮)]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(ShowBaseSalary));
Thread thread2 = new Thread(new ThreadStart(ShowBonusAndSalary));
thread1.Start();
thread2.Start();
}
private void ShowBaseSalary()
{
MessageBox.Show(Test.GetBaseSalary());
}
private void ShowBonusAndSalary()
{
MessageBox.Show(Test.GetBonusAndSalary(500));
}
}
class Test
{
private static Employee employee = new Employee(29); //实例化变量的时间可能很长,如何保证线程安全(线程1实例过程中,线程1有可能执行到该代码吗?)??
private int m = 0;
public static string GetBaseSalary()
{
return employee.GetBaseSalary();
}
public static string GetBonusAndSalary(int nBonus)
{
return employee.GetBonusAndSalary(nBonus);
}
}
[Employee.cs]
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication2
{
class Employee
{
private const int SALARYLEVEL = 15; //工资基数
private int _baseSalary;
public Employee(int ages)
{
Random rnd = new Random();
int nValue = rnd.Next(100);
while (nValue !=26) //模拟类实例化执行很长时间
{
_baseSalary = ages * SALARYLEVEL * nValue / 100;
nValue = rnd.Next(100);
System.Threading.Thread.Sleep(1000);
}
}
//获取加上奖金的工资
public string GetBonusAndSalary(int nBonus)
{
return Convert.ToString(_baseSalary + nBonus);
}
//获取基本工资
public string GetBaseSalary()
{
return _baseSalary.ToString();
}
}
}
另,C#中不需要用锁的线程安全的Singleton设计模式!
典型的线程安全的Singleton实现是使用double-checked locking的实现,但是在.NET中我们并不需要使用double-checked locking就可以优雅地实现Singleton模式。
这个优美的Singleton实现基础是.NET对运行期初始化的完整定义。它的优美之处是不需要典型的double-checked locking。
当CLR加载class Singleton的时候,因为Singleton没有static variables需要被初始化,所以Singleton的初始化其实什么也没做。而对static class LazyHolder来说,直到它被执行的时候才会被初始化。而static class LazyHolder只有载Singleton.GetInstance()被执行的时候才会执行到。当第一次调用GetInstance()的时候 CLR才会加载和初始化LazyHolder这个class。对于LazyHolder class的初始化就是对static variable Instance的初始化。而Instance的初始化就是执行Singleton的private constructor。因为.NET保证class的initialization是serial的,就是说在加载和初始化的过程中我们不需要自己做同步。因为初始化过程是一个serial的操作,所以对于后面的GetInstance,我们不需要做任何同步,它也会返回一个正确初始化的 Singleton对象。
实现代码:
2 {
3 private static class LazyHolder
4 {
5 public static readonly Singleton Instance = new Singleton();
6 }
7
8 private Singleton()
9 {
10 }
11
12 public static Singleton GetInstance()
13 {
14 return LazyHolder.Instance;
15 }
16 }
public class Singleton
{
private static readonly Singleton Instance = new Singleton();
static Singleton()
{
}
public static Singleton GetInstance()
{
return Instance;
}
}
这个吧!
sealed class Singleton
{
private Singleton();
public static readonly Singleton Instance=new Singleton();
}
1,私有实例构造器保证了不会被实例化.
2,sealed保证了不会被继承
3,readonly保证了实例化后的自动不会被修改
4,static 自动保证了线程安全的实例化.
5,没有静态构造函数保证了编译器产生的构造函数是BeforeFieldInit,达到了lazy-load目的.
不过我觉的这种情况下为什么不要static class了?非要用Singleton模式?
public class Singleton
{
private static Singleton Instance = null;
private Singleton()
{
}
static Singleton()
{
Instance = new Singleton();
}
public static Singleton GetInstance()
{
return Singleton.Instance;
}
}