多线程常见处理
多线程间 任务取消
//多线程并发任务,某个失败后,希望通知别的线程,都停下来,how? //Thread.Abort--终止线程;向当前线程抛一个异常然后终结任务;线程属于OS资源,可能不会立即停下来 //Task不能外部终止任务,只能自己终止自己(上帝才能打败自己) //cts有个bool属性IsCancellationRequested 初始化是false //调用Cancel方法后变成true(不能再变回去),可以重复cancel try { CancellationTokenSource cts = new CancellationTokenSource(); List<Task> taskList = new List<Task>(); for (int i = 0; i < 50; i++) { string name = $"btnThreadCore_Click_{i}"; taskList.Add(Task.Run(() => { try { if (!cts.IsCancellationRequested) Console.WriteLine($"This is {name} 开始 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); Thread.Sleep(new Random().Next(50, 100)); if (name.Equals("btnThreadCore_Click_11")) { throw new Exception("btnThreadCore_Click_11异常"); } else if (name.Equals("btnThreadCore_Click_12")) { throw new Exception("btnThreadCore_Click_12异常"); } else if (name.Equals("btnThreadCore_Click_13")) { cts.Cancel(); } if (!cts.IsCancellationRequested) { Console.WriteLine($"This is {name}成功结束 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); } else { Console.WriteLine($"This is {name}中途停止 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); return; } } catch (Exception ex) { Console.WriteLine(ex.Message); cts.Cancel(); } }, cts.Token)); //传入token会在Cancel以后 全部取消任务
} //1 准备cts 2 try-catch-cancel 3 Action要随时判断IsCancellationRequested //尽快停止,肯定有延迟,在判断环节才会结束 Task.WaitAll(taskList.ToArray()); //如果线程还没启动,能不能就别启动了? //1 启动线程传递Token 2 异常抓取 //在Cancel时还没有启动的任务,就不启动了;也是抛异常,cts.Token.ThrowIfCancellationRequested } catch (AggregateException aex) { foreach (var exception in aex.InnerExceptions) { Console.WriteLine(exception.Message); } } catch (Exception ex) { Console.WriteLine(ex.Message); }
多线程 内存锁
//1 Lock解决多线程冲突 //Lock是语法糖,Monitor.Enter,占据一个引用,别的线程就只能等着 //推荐锁是private static readonly object, // A不能是Null,可以编译不能运行; //B 不推荐lock(this),外面如果也要用实例,就冲突了 Test test = new Test(); Task.Delay(1000).ContinueWith(t => { lock (test) { Console.WriteLine("*********Start**********"); Thread.Sleep(5000); Console.WriteLine("*********End**********"); } }); test.DoTest(); //C 不应该是string; string在内存分配上是重用的,会冲突 //D Lock里面的代码不要太多,这里是单线程的 Test test = new Test(); string student = "水煮鱼"; Task.Delay(1000).ContinueWith(t => { lock (student) { Console.WriteLine("*********Start**********"); Thread.Sleep(5000); Console.WriteLine("*********End**********"); } }); test.DoTestString(); public void DoTestString() { lock (this.Name) //递归调用,lock this 会不会死锁? 98%说会! 不会死锁! //这里是同一个线程,这个引用就是被这个线程所占据 { Thread.Sleep(500); this.iDoTestNum++; if (DateTime.Now.Day < 28 && this.iDoTestNum < 10) { Console.WriteLine($"This is {this.iDoTestNum}次 {DateTime.Now.Day}"); this.DoTestString(); } else { Console.WriteLine("28号,课程结束!!"); } } } private int iDoTestNum = 0; private string Name = "水煮鱼";
多线程 安全集合
System.Collections.Concurrent.ConcurrentQueue<int>