/// 1 多异常处理和线程取消 /// 2 多线程的临时变量 /// 3 线程安全和锁lock class Program { static void Main(string[] args) { #region 多线程异常处理 //{ // try // { // List<Task> taskList = new List<Task>(); // for (int i = 0; i < 100; i++) // { // string name = $"btnThreadCore_Click_{i}"; // taskList.Add(Task.Run(() => // { // 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_38")) // { // throw new Exception("btnThreadCore_Click_38异常"); // } // Console.WriteLine($"This is {name}成功 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); // })); // } // //多线程里面抛出的异常,会终结当前线程;但是不会影响别的线程; // //那线程异常哪里去了? 被吞了, // //假如我想获取异常信息,还需要通知别的线程 // Task.WaitAll(taskList.ToArray());//1 可以捕获到线程的异常 // } // catch (AggregateException aex)//2 需要try-catch-AggregateException // { // foreach (var exception in aex.InnerExceptions) // { // Console.WriteLine(exception.Message); // } // } // catch (Exception ex)//可以多catch 先具体再全部 // { // Console.WriteLine(ex); // } // //线程异常后经常是需要通知别的线程,而不是等到WaitAll,问题就是要线程取消 // //工作中常规建议:多线程的委托里面不允许异常,包一层try-catch,然后记录下来异常信息,完成需要的操作 //} #endregion #region 线程取消 //{ // //多线程并发任务,某个失败后,希望通知别的线程,都停下来,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")) // { // throw new Exception("btnThreadCore_Click_13异常"); // } // 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)); // } // //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); // } //} #endregion #region 临时变量 { ////for (int i = 0; i < 5; i++) ////{ //// Task.Run(() => //// { //// Console.WriteLine($"This is btnThreadCore_Click_{i} ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); //// }); ////} ////临时变量问题,线程是非阻塞的,延迟启动的;线程执行的时候,i已经是5了 ////k是闭包里面的变量,每次循环都有一个独立的k ////5个k变量 1个i变量 //for (int i = 0; i < 5; i++) //{ // int k = i; // Task.Run(() => // { // Console.WriteLine($"This is btnThreadCore_Click_{i}_{k} ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); // }); //} } #endregion #region 线程安全&lock { //线程安全:如果你的代码在进程中有多个线程同时运行这一段,如果每次运行的结果都跟单线程运行时的结果一致,那么就是线程安全的 //线程安全问题一般都是有全局变量/共享变量/静态变量/硬盘文件/数据库的值,只要多线程都能访问和修改 //发生是因为多个线程相同操作,出现了覆盖,怎么解决? //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里面的代码不要太多,这里是单线程的 //2 线程安全集合 //System.Collections.Concurrent.ConcurrentQueue<int> //3 数据分拆,避免多线程操作同一个数据;又安全又高效 //int iNumSync = 0; //int iNumAsync = 0; //for (int i = 0; i < 10000; i++) //{ // iNumSync++; //} //for (int i = 0; i < 10000; i++) //{ // Task.Run(() => // { // iNumAsync++; // }); // //Task.Run(() => // //{ // // lock (Form_Lock)//任意时刻只有一个线程能进入方法块儿,这不就变成了单线程结果就是10000 // // { // // iNumAsync++; // // } // //}); //} //iNumSync 和 iNumAsync分别是多少 10000 1到10000以内 //for (int i = 0; i < 10000; i++) //{ // int k = i; // Task.Run(() => this.iListAsync.Add(k)); //} //Thread.Sleep(5 * 1000); //Console.WriteLine($"iNumSync={iNumSync} iNumAsync={iNumAsync}"); } #endregion #region lock this和lock string { Test test = new Test(); ////lock同一个实例就会冲突 //test.DoTest(); //Task.Delay(1000).ContinueWith(t => //{ // lock (test) //DoTest方法执行完 才会执行这样 // { // Console.WriteLine("*********Start**********"); // Thread.Sleep(5000); // Console.WriteLine("*********End**********"); // } //}); //string在内存分配上是重用的,也会冲突 string student = "张三"; Task.Delay(1000).ContinueWith(t => { lock (student) { Console.WriteLine("*********Start**********"); Thread.Sleep(5000); Console.WriteLine("*********End**********"); } }); test.DoTestString(); } #endregion Console.ReadLine(); } static readonly object Form_Lock = new object(); } public class Test { /// <summary> /// /// </summary> public void DoTest() { lock (this) //递归调用,lock this 不会死锁! //这里是同一个线程,这个引用就是被这个线程所占据 { Thread.Sleep(500); this.iDoTestNum++; if (DateTime.Now.Day < 28 && this.iDoTestNum < 10) { Console.WriteLine($"This is {this.iDoTestNum}次 {DateTime.Now.Day}"); this.DoTest(); } else { Console.WriteLine("循环结束!!"); } } } public void DoTestString() { lock (this.Name) { 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("循环结束!!"); } } } private int iDoTestNum = 0; private string Name = "张三"; }