同步对象 Lock
对于任何一个对象来说,他在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,他指向CLR中的SyncBlock Cache区域中的一个SyncBlock.什么意思呢?就是说,当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即线程使用Monitor.Exit(object)将索引变为负数。
1.不建议lock public性质的类或者属性。例如 lock(this) 或者lock pubilic属性。 容易导致死锁。
2.建议lock住私有的静态成员变量。
3.lock住私有非静态变量的,只能保护实例的成员数据,如果临界区是非实例本身的资源或者行为,需要谨慎。
另外:
lock(this) 慎用,因为锁定的是整个对象实例,外部也可以锁定该对象,会导致死锁。
namespace Namespace1 { class C1 { privatebool deadlocked= true; //这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问 publicvoid LockMe(object o) { lock (this) { while(deadlocked) { deadlocked = (bool)o; Console.WriteLine("Foo: I am locked :("); Thread.Sleep(500); } } } //所有线程都可以同时访问的方法 publicvoid 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()都没有执行。
class C1 { privatebool deadlocked= true; privateobject locker= newobject(); //这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问 publicvoid LockMe(object o) { lock (locker) { while(deadlocked) { deadlocked = (bool)o; Console.WriteLine("Foo: I am locked :("); Thread.Sleep(500); } } } //所有线程都可以同时访问的方法 publicvoid 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); } } }
这次我们使用一个私有成员作为锁定变量(locker),在LockMe中仅仅锁定这个私有locker,而不是整个对象。这时候重新运行程序,可以看到虽然t1出现了死锁,DoNotLockMe()仍然可以由主线程访问;LockMe()依然不能访问,原因是其中锁定的locker还没有被t1释放。
所以,lock(this)的缺点是,容易导致死锁;锁的粒度太大,用lock(this)了,对象内的其他非线程安全的方法不能被访问。