为何不要lock(this) ,锁定变量最好是readonly

来自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=392
GPS平台、网站建设、软件开发、系统运维,找森大网络科技!
http://cnsendnet.taobao.comphp

一. 为何要lock,lock了什么?安全

当咱们使用线程的时候,效率最高的方式固然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不一样的线程都须要访问某个资源的时候,就须要同步机制了,也就是说当对同一个资源进行读写的时候,咱们要使该资源在同一时刻只能被一个线程操做,以确保每一个操做都是有效即时的,也即保证其操做的原子性。lock是C#中最经常使用的同步方式,格式为lock(objectA){codeB} 。网络

lock(objectA){codeB} 看似简单,实际上有三个意思,这对于适当地使用它相当重要:运维

  1. objectA被lock了吗?没有则由我来lock,不然一直等待,直至objectA被释放。
  2. lock之后在执行codeB的期间其余线程不能调用codeB,也不能使用objectA。
  3. 执行完codeB以后释放objectA,而且codeB能够被其余线程访问。

二. lock(this)怎么了?异步

咱们看一个例子:ide

using System;
using System.Threading;

namespace Namespace1
{
    class C1
     {
        private bool deadlocked= true;

        //这个方法用到了lock,咱们但愿lock的代码在同一时刻只能由一个线程访问
        public void LockMe(object o)
         {
            lock (this)
             {
                while(deadlocked)
                 {
                     deadlocked = (bool)o;
                     Console.WriteLine("Foo: I am locked :(");
                     Thread.Sleep(500);
                 }
             }
         }

        //全部线程均可以同时访问的方法
        public void DoNotLockMe()
         {
             Console.WriteLine("I am not locked :)");
         }
     }

    class Program
     {
        staticvoid Main(string[] args)
         {
             C1 c1 =new C1();

            //在t1线程中调用LockMe,并将deadlock设为true(将出现死锁)
             Thread t1= new Thread(c1.LockMe);
             t1.Start(true);
             Thread.Sleep(100);

            //在主线程中lock c1
            lock (c1)
             {
                //调用没有被lock的方法
                 c1.DoNotLockMe();
                //调用被lock的方法,并试图将deadlock解除
                 c1.LockMe(false);
             }
         }
     }

在t1线程中,LockMe调用了lock(this), 也就是Main函数中的c1,这时候在主线程中调用lock(c1)时,必需要等待t1中的lock块执行完毕以后才能访问c1,即全部c1相关的操做都没法完成,因而咱们看到连c1.DoNotLockMe()都没有执行。函数

把C1的代码稍做改动:网站

class C1
     {
        privatebool deadlocked= true;
        private object locker= new object();

        //这个方法用到了lock,咱们但愿lock的代码在同一时刻只能由一个线程访问
        public void LockMe(object o)
         {
            lock (locker)
             {
                while(deadlocked)
                 {
                     deadlocked = (bool)o;
                     Console.WriteLine("Foo: I am locked :(");
                     Thread.Sleep(500);
                 }
             }
         }

        //全部线程均可以同时访问的方法
        public void DoNotLockMe()
         {
             Console.WriteLine("I am not locked :)");
         }
     }

此次咱们使用一个私有成员做为锁定变量(locker),在LockMe中仅仅锁定这个私有locker,而不是整个对象。这时候从新运行程序,能够看到虽然t1出现了死锁,DoNotLockMe()仍然能够由主线程访问;LockMe()依然不能访问,缘由是其中锁定的locker尚未被t1释放。this

关键点:spa

  1. lock(this)的缺点就是在一个线程锁定某对象以后致使整个对象没法被其余线程访问。
  2. 锁定的不单单是lock段里的代码,锁自己也是线程安全的。
  3. 咱们应该使用不影响其余操做的私有对象做为locker。
  4. 在使用lock的时候,被lock的对象(locker)必定要是引用类型的,若是是值类型,将致使每次lock的时候都会将该对象装箱为一个新的引用对象(事实上若是使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。

kenny add

而对于Monitor,发现它的静态方法Enter(object obj)有一个异常类型ArgumentNullException,
执行lock(null对象 )处,抛出未处理的异常:System.ArgumentNullException: 值不能为空!
在代码段中修改锁定对象,会出现 blance<0的状况,并会抛出异常
private static readonly object obj = new object();
为何要设置成只读的呢?这是由于若是在lock代码段中改变obj的值,其它线程就畅通无阻了,由于互斥锁的对象变了,object.ReferenceEquals必然返回false。
因此把上面的修改为private static readonly

来自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=392
GPS平台、网站建设、软件开发、系统运维,找森大网络科技!
http://cnsendnet.taobao.com

相关文章
相关标签/搜索