C#多线程锁lock和Monitor怎么用
本文小编为大家详细介绍“C#多线程锁lock和Monitor怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#多线程锁lock和Monitor怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
1,Locklock 用于读一个引用类型进行加锁,同一时刻内只有一个线程能够访问此对象。lock 是语法糖,是通过 Monitor 来实现的。
Lock 锁定的对象,应该是静态的引用类型(字符串除外)。
实际上字符串也可以作为锁的对象使用,只是由于字符串对象的特殊性,可能会造成不同位置的不同线程冲突。
如果你能保证字符串的唯一性,例如 Guid 生成的字符串,也是可以作为锁的对象使用的(但不建议)。
锁的对象也不一定要静态才行,也可以通过类实例的成员变量,作为锁对象。
lock 是 Monitor 的语法糖,生成的代码对比:
lock(x){//Yourcode...}
object__lockObj=x;bool__lockWasTaken=false;try{System.Threading.Monitor.Enter(__lockObj,ref__lockWasTaken);//Yourcode...}finally{if(__lockWasTaken)System.Threading.Monitor.Exit(__lockObj);}
这里先不理会 Monitor,后面再说。
lock 编写实例首先,如果像下面这样写的话,拉出去打 si 吧。
publicvoidMyLock(){objecto=newobject();lock(o){//}}
下面编写一个简单的锁,示例如下:
classProgram{privatestaticobjectobj=newobject();privatestaticintsum=0;staticvoidMain(string[]args){Threadthread1=newThread(Sum1);thread1.Start();Threadthread2=newThread(Sum2);thread2.Start();while(true){Console.WriteLine($"{DateTime.Now.ToString()}:"+sum);Thread.Sleep(TimeSpan.FromSeconds(1));}}publicstaticvoidSum1(){sum=0;lock(obj){for(inti=0;i<10;i++){sum+=i;Console.WriteLine("Sum1");Thread.Sleep(TimeSpan.FromSeconds(2));}}}publicstaticvoidSum2(){sum=0;lock(obj){for(inti=0;i<10;i++){sum+=1;Console.WriteLine("Sum2");Thread.Sleep(TimeSpan.FromSeconds(2));}}}}
类将自己设置为锁, 这可以防止恶意代码对公共对象采用做锁。
例如:
publicvoidAccess(){lock(this){}}
锁可以阻止其它线程执行锁块(lock(o){})中的代码,当锁定时,其它线程必须等待锁中的线程执行完成并释放锁。但是这可能会给程序带来性能影响。
锁不太适合I/O场景,例如文件I/O,繁杂的计算或者操作比较持久的过程,会给程序带来很大的性能损失。
10 种优化锁的性能方法:http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
2,Monitor此对象提供同步访问对象的机制;Monotor 是一个静态类型,其方法比较少,常用方法如下:
下面是一个很简单的示例:
privatestaticobjectobj=newobject();privatestaticboolacquiredLock=false;publicstaticvoidTest(){try{Monitor.Enter(obj,refacquiredLock);}catch{}finally{if(acquiredLock)Monitor.Exit(obj);}}
Monitor.Enter
锁定 obj 这个对象,并且设置 acquiredLock 为 true,告诉别人 obj 已经被锁定。
最后结束时,判断 acquiredLock ,释放锁,并设置 acquiredLock 为 false。
解释一下临界区:指被某些符号包围的范围。例如{}
内。
Monitor 对象的 Enter 和 Exit 方法来标记临界区的开头和结尾。
Enter()
方法获取锁后,能够保证只有单个线程能够使用临界区中的代码。使用 Monitor 类,最好搭配try{...}catch{...}finally{...}
来使用,因为如果获取到锁但是没有释放锁的话,会导致其它线程无限阻塞,即发生死锁。
一般来说,lock 关键字够用了。
示例下面示范了多个线程如何使用 Monitor 来实现锁:
privatestaticobjectobj=newobject();privatestaticboolacquiredLock=false;staticvoidMain(string[]args){newThread(Test1).Start();Thread.Sleep(1000);newThread(Test2).Start();}publicstaticvoidTest1(){try{Monitor.Enter(obj,refacquiredLock);for(inti=0;i<10;i++){Console.WriteLine("Test1正在锁定资源");Thread.Sleep(1000);}}catch{}finally{if(acquiredLock)Monitor.Exit(obj);Console.WriteLine("Test1已经释放资源");}}publicstaticvoidTest2(){boolisGetLock=false;Monitor.Enter(obj);try{Monitor.Enter(obj,refacquiredLock);for(inti=0;i<10;i++){Console.WriteLine("Test2正在锁定资源");Thread.Sleep(1000);}}catch{}finally{if(acquiredLock)Monitor.Exit(obj);Console.WriteLine("Test2已经释放资源");}}设置获取锁的时效
如果对象已经被锁定,另一个线程使用Monitor.Enter
对象,就会一直等待另一个线程解除锁定。
但是,如果一个线程发生问题或者出现死锁的情况,锁一直被锁定呢?或者线程具有时效性,超过一段时间不执行,已经没有了意义呢?
我们可以通过Monitor.TryEnter()
来设置等待时间,超过一段时间后,如果锁还没有释放,就会返回 false。
改造上面的示例如下:
privatestaticobjectobj=newobject();privatestaticboolacquiredLock=false;staticvoidMain(string[]args){newThread(Test1).Start();Thread.Sleep(1000);newThread(Test2).Start();}publicstaticvoidTest1(){try{Monitor.Enter(obj,refacquiredLock);for(inti=0;i<10;i++){Console.WriteLine("Test1正在锁定资源");Thread.Sleep(1000);}}catch{}finally{if(acquiredLock)Monitor.Exit(obj);Console.WriteLine("Test1已经释放资源");}}publicstaticvoidTest2(){boolisGetLock=false;isGetLock=Monitor.TryEnter(obj,500);if(isGetLock==false){Console.WriteLine("锁还没有释放,我不干活了");return;}try{Monitor.Enter(obj,refacquiredLock);for(inti=0;i<10;i++){Console.WriteLine("Test2正在锁定资源");Thread.Sleep(1000);}}catch{}finally{if(acquiredLock)Monitor.Exit(obj);Console.WriteLine("Test2已经释放资源");}}
读到这里,这篇“C#多线程锁lock和Monitor怎么用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。