异步多线程(六)lock锁
非线程安全
引发线程安全的原因是,多个线程可以同时操作全局变量/共享变量/静态变量/磁盘文件/数据库的值就可能存在线程安全问题,因为多个线程操作,出现了覆盖。List集合也是非线程安全的。
线程安全
多线程同时运行,如果每次运行的结果和单线程运行的结果一致,那么就是线程安全的。
Lock锁
概念
解决多线程冲突问题,Lock是语法糖,Monitor.Enter,占据一个引用,别的线程就只能等着。锁的本质就是把多线程在某些特定场合下变成单线程,来完成某些特定操作。
在日常工作中,有些是特定需要锁的,更多的时候我们可以把数据拆分,避免多线程操作同一个数据,这样既安全又高效。例如有10000个任务,那可以进行拆分,某个线程执行1-100给任务。
Lock锁不允许的类型
String:string在内存分配上是重用的,会冲突。这么话应该这么理解,比如你定义一个变量name=”张三”,再定义一个变量temeName=”张三”,那么在C#内存分配时会指向同一个引用。
Null:可以运行,不能编译通过。
详解Lock
Lock锁的两钟方式:
Lock(this)不推荐的锁如果只是当前内部实例使用,则不会冲突。外面如果也要用实例,就冲突了。
public void DoTest() { //递归调用 lock(this)不会死锁,原因:这里是同一个线程,这个引用就是被这个线程锁占据 //锁的本质是让这个引用不会被其他线程所占据 lock (this) { Thread.Sleep(500); iDotestNum++; if (DateTime.Now.Day < 13&&iDotestNum<10) { Console.WriteLine($"这是第{DateTime.Now.Day}天,第{iDotestNum}次"); this.DoTest(); } else { Console.WriteLine("结束啦"); } } } private int iDotestNum = 0;
外部也要使用实例:
Test test = new Test(); Task.Delay(1000).ContinueWith(t => { lock (test)//此处是锁住test实例 { Console.WriteLine("*********Start**********"); Thread.Sleep(5000); Console.WriteLine("*********End**********"); } }); //会冲突,因为DoTest方法里的lock(this)就是锁住当前的实例即:test test.DoTest(); public class Test{ public void DoTest() { //递归调用 lock(this)不会死锁,原因:这里是同一个线程,这个引用就是被这个线程锁占据 //锁的本质是让这个引用不会被其他线程所占据 lock (this) { Thread.Sleep(500); iDotestNum++; if (DateTime.Now.Day < 13&&iDotestNum<10) { Console.WriteLine($"这是第{DateTime.Now.Day}天,第{iDotestNum}次"); this.DoTest(); } else { Console.WriteLine("结束啦"); } } } private int iDotestNum = 0; }
标准锁,微软推荐的标准锁
private static readonly object Form_Lock = new object();
lock(Form_Lock);
#region 标准锁 for (int i = 0; i < 1000; i++) { Task.Run(() => { lock (Form_Lock)// 任意时刻只有一个线程能进入方法块儿 { intAsyncNum++; } }); } Console.WriteLine($"intAsyncNum={intAsyncNum}"); #endregion