1. 线程的异常处理
我们经常会遇到一个场景,开启了多个线程,其中一个线程报错,导致整个程序崩溃。这并不是我们想要的,我需要的结果是,其中一个线程报错,默默的记录下,其它线程正常进行,保证程序整体可以走下来。
解决方案:给函数体加try-catch,只让报错线程异常,其它线程可以正常进行。
1 private void button7_Click(object sender, EventArgs e) 2 { 3 Stopwatch watch = new Stopwatch(); 4 watch.Start(); 5 Console.WriteLine("----------------- 八.线程的特殊处理 --------------------------"); 6 Console.WriteLine("----------------- button7_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId); 7 try 8 { 9 10 TaskFactory taskFactory = new TaskFactory(); 11 List<Task> taskList = new List<Task>(); 12 13 #region 01-异常处理 14 { 15 for (int i = 0; i < 5; i++) 16 { 17 string name = string.Format("button_Click{0}", i); 18 Action<object> act = t => 19 { 20 try 21 { 22 //模拟报错 23 if (t.ToString().Equals("button_Click2")) 24 { 25 throw new Exception(string.Format("{0} 执行失败", t)); 26 } 27 Console.WriteLine("{0} 执行成功", t); 28 } 29 catch (Exception ex) 30 { 31 Console.WriteLine(ex.Message); 32 } 33 }; 34 Task task = taskFactory.StartNew(act, name); 35 taskList.Add(task); 36 } 37 //线程等待 38 Task.WaitAll(taskList.ToArray()); 39 } 40 #endregion 41 44 } 45 catch (AggregateException aes) 46 { 47 foreach (var item in aes.InnerExceptions) 48 { 49 Console.WriteLine(item.Message); 50 } 51 } 52 catch (Exception ex) 53 { 54 Console.WriteLine(ex.Message); 55 } 56 57 watch.Stop(); 58 Console.WriteLine("----------------- button7_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds); 59 60 }
运行结果:
2. 线程的取消
线程不能主动取消,只能通过信号量的形式进行检查,接到取消指令后,后续的线程都将取消。
1 { 2 //Task不能主动取消,只能通过信号量检查的方式 3 CancellationTokenSource cts = new CancellationTokenSource(); 4 for (int i = 0; i < 10; i++) 5 { 6 string name = string.Format("button_Click{0}", i); 7 Action<object> act = t => 8 { 9 try 10 { 11 Thread.Sleep(2000); 12 //模拟报错 13 if (t.ToString().Equals("button_Click2")) 14 { 15 throw new Exception(string.Format("{0} 执行失败", t)); 16 } 17 if (cts.IsCancellationRequested) 18 { 19 Console.WriteLine("{0} 放弃执行", t); 20 } 21 else 22 { 23 Console.WriteLine("{0} 执行成功", t); 24 } 25 } 26 catch (Exception ex) 27 { 28 cts.Cancel(); //凡是在 button_Click2 的执行线程后执行的,均被放弃了执行 29 Console.WriteLine(ex.Message); 30 } 31 }; 32 Task task = taskFactory.StartNew(act, name, cts.Token); 33 taskList.Add(task); 34 } 35 //线程等待 36 Task.WaitAll(taskList.ToArray()); 37 }
运行结果:
3. 多线程临时变量
1 { 2 for (int i = 0; i < 5; i++) 3 { 4 int k = i; 5 Action act = () => 6 { 7 Console.WriteLine(k); //0,1,2,3,4 不一定按照顺序 8 Console.WriteLine(i); //全为5 9 }; 10 act.BeginInvoke(null, null); 11 } 12 }
4. 线程安全
当多个线程同时操控一个全局变量时,需要加锁,来保证线程安全。其中锁变量为 私有的静态object变量。eg: private static object btnThreadCore_Click_Lock = new object();
1 { 2 for (int i = 0; i < 10000; i++) 3 { 4 Task task = taskFactory.StartNew(() => 5 { 6 //只有方法外的变量需要加锁 7 int k = 2; 8 lock (btnThreadCore_Click_Lock) 9 { 10 this.TotalCount += 1; 11 } 12 }); 13 taskList.Add(task); 14 } 15 //线程等待 16 Task.WaitAll(taskList.ToArray()); 17 //测试输出结果 18 Console.WriteLine(this.TotalCount); //当不加线程锁的时候,结果不一定是10000,加锁后,一定为10000 19 }
5. 委托的几种赋值形式
三个方法:TestThread()、TestThread2(string name)、TestThread(string name,string pwd)。
形式一:声明的委托参数和要赋值的函数参数意义对应
eg: Action act1=TestThread ;
Action<string> act2=TestThread2;
Action<string,string> act3=TestThread3;
注意:这种形式,需要在委托被调用的时候将函数所需的参数值传递进去。
形式二: 声明无参数委托,但利用()=>进行将有参数的函数转换,可以直接将参数所需的值传递进去,委托调用的时候,将不需要再传递了。
eg: Action act1=TestThread ;
Action act2=()=>TestThread2("测试参数1");
Action act3=()=>TestThread3("测试参数1","测试参数2");
注意:这种形式,实际上就是直接将函数体赋值给委托,但又把一个函数当做内容传递到了这个函数体内,实际上
Action act2=()=>TestThread2("测试参数1"); 等价于
Action act2=()=>{
TestThread2("测试参数1");
}。
形式三: 直接将函数体赋值给委托,然后函数体中的内容为一个新的函数,这种形式,需要在委托被调用的时候将函数所需的参数值传递进去。
eg: Action act1=TestThread ;
Action act2=t=>TestThread2(t);
Action act3=(m,n)=>TestThread3(m,n);
注意:这种形式,实际上就是直接将函数体赋值给委托,但又把一个函数当做内容传递到了这个函数体内,实际上
Action act2=t=>TestThread2(t); 等价于
Action act2=(t)=>{
TestThread2("测试参数1");
}。