您当前的位置: 首页 >  安全

染指流年灬

暂无认证

  • 3浏览

    0关注

    194博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

关于C#的Lock关键字实现线程安全的单例模式

染指流年灬 发布时间:2020-06-25 15:46:44 ,浏览量:3

非继承自其他类的单例模式写法
public class Singleton where T : Singleton{

    private static T _instance;

    public static T Instance
    {
        get {
            if (_instance == null)
            {
                _instance = Activator.CreateInstance();
            }
            return _instance;
        }
    }
}

写法

public class HttpManager : Singleton

关于C#的Lock关键字实现线程安全的单例模式

在看pureMVC使用的时候注意到有个关于线程安全的单例模式的使用用到了Lock关键词,这里对其进行一下记录。代码如下,


protected static readonly object m_staticSyncRoot;

public new static IFacade Instance
    {
        get
        {
            if(m_instance == null)
            {
                lock(m_staticSyncRoot)
                {
                    if (m_instance == null)
                    {
                        Debug.Log("ApplicationFacade");
                        m_instance = new ApplicationFacade();
                    }
                }
            }
            return m_instance;
        }
    }

这里用到了一个关键词lock,lock通过锁定一个实例,对一个代码段进行单线程的访问控制,即不论在任何时候只能有一个线程对lock大括号的代码段进行访问,其他线程运行到这个代码段的时候只能等待(或者说阻塞)在进入lock之前。 lock锁定一个对象的原理如下:

  • 对于任何一个对象,其在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,索引指向CLR(公共语言运行时)中的SyncBlock Cache区域中的一个SyncBlock(SyncBlock简单来说就是Monitor.Enter用来锁定一个实例当前仅能被一个线程访问的工具).
  • Monitor.Enter和Monitor.Exit,这俩个函数是c#库的函数,他们的作用分别是锁定一个资源,使得其他线程不能被访问,以及解锁这个资源。当执行Monitor.Enter(object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即当前锁定线程使用Monitor.Exit(object)将object的索引变为负数。
  • lock语句根本使用的是Monitor.Enter和Monitor.Exit,即在大括号开始时对lock操作的对象传进Monitor.Enter,这里是m_staticSyncRoot,在大括号结束的时候再使用Monitor.Ente对m_staticSyncRoot进行解锁。

在上述代码中,lock的外部和内部使用到了两个看似重复的if判断,两者实际上都是有各自的功能的。 lock外部的if主要简单的对m_staticSyncRoot进行判空判断,决定是否要进入lock语句块。 在上文中黑体字突出的等待中可以发现,如果有多个线程同时执行上述代码,第一个线程进入lock块进行操作时,其余的线程都是在进入第一个if后进入lock之前等待,第一个进入lock块的线程进行初始化结束之后,其实这时m_staticSyncRoot已经指向某个实例了, 第二个进来的线程如果不进行判断,则还会再次进行实例化,所以在lock里面的if实际上是有用的。

关于lock的使用注意事项:

  • lock的对象不能是null,关于lock对一个对象进行上锁的机理中可以发现需要访问对象的内存的第二部分,而null值什么都没有,所以用null进行lock会报错。
  • 在使用lock的时候,被lock的对象(locker)一定要是引用类型的(即类的实例,在内存堆中生成,需要由垃圾回收机制进行销毁),如果是值类型(如int类型等基本类型,在内存栈中生成,使用完即刻销毁),将导致每次lock的时候都会将该值类型的对象装箱为一个新的引用对象(事实上如果使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。
  • 不要使用lock(this),this指的是当前的实例,当锁住后导致别的进程也无法访问整个实例,应lock一个专门用于lock的私有或者保护类型的成员变量或者私有或者保护类型的静态变量
关注
打赏
1665909078
查看更多评论
立即登录/注册

微信扫码登录

0.0403s