.Net进阶系列(15)-异步多线程(线程的特殊处理和深究委托赋值)(被替换)

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");

           }。

 

posted @ 2017-06-25 11:23  Yaopengfei  阅读(398)  评论(8编辑  收藏  举报