今天继续讲讲线程的异常处理、线程取消、多线程的临时变量和线程安全lock的问题。
1、异步处理。
一般来说如果是同步方法的异步处理,我们大多都是try catch住,但是异步方法应该怎么做呢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #region 异常处理 //多线程的委托是不允许异常的, try catch包住,写下日志 for ( int i = 0; i < 20; i++) { string name = string .Format( "btnThreadCore_Click{0}" , i); Action< object > act = t => { try { Thread.Sleep(2000); if (t.ToString().Equals( "btnThreadCore_Click11" )) { throw new Exception( string .Format( "{0} 执行失败" , t)); } if (t.ToString().Equals( "btnThreadCore_Click12" )) { throw new Exception( string .Format( "{0} 执行失败" , t)); } Console.WriteLine( "{0} 执行成功" , t); } catch (Exception ex) { Console.WriteLine(ex.Message); } }; taskList.Add(taskFactory.StartNew(act, name)); } Task.WaitAll(taskList.ToArray()); #endregion |
2、线程取消。
Task不能主动取消,就好比向CPU发起了一个请求,但是你中途想中断这个请求,在正常情况下是做不到的,
同样,线程也做不到这一点,只有通过检测信号量的方式,来检测,使其线程本身来做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | CancellationTokenSource cts = new CancellationTokenSource(); for ( int i = 0; i < 40; i++) { string name = string .Format( "btnThreadCore_Click{0}" , i); Action< object > act = t => { try { Thread.Sleep(2000); if (t.ToString().Equals( "btnThreadCore_Click11" )) { throw new Exception( string .Format( "{0} 执行失败" , t)); } if (t.ToString().Equals( "btnThreadCore_Click12" )) { throw new Exception( string .Format( "{0} 执行失败" , t)); } if (cts.IsCancellationRequested) { Console.WriteLine( "{0} 放弃执行" , t); } else { Console.WriteLine( "{0} 执行成功" , t); } } catch (Exception ex) { cts.Cancel(); Console.WriteLine(ex.Message); } }; taskList.Add(taskFactory.StartNew(act, name); //没有启动的任务 在Cancel后放弃启动 } Task.WaitAll(taskList.ToArray()); |
通过代码运行可以看到会出现三种结果,那么这三种结果是什么情况下出现的呢,
执行成功和执行失败这两种情况应该好理解,,放弃执行是在执行失败出现时,捕获住了异常信息,然后通过cts.Cancel();使信号量改变,
然后通过cts.IsCancellationRequested判断,这就出现了只要是出现了执行失败,后面都是放弃执行的情况。
3、多线程临时变量
1 2 3 4 5 6 7 8 9 10 | for ( int i = 0; i < 5; i++) { new Action(() => { //Thread.Sleep(100); Console.WriteLine(i); }).BeginInvoke( null , null ); } |
执行这么一段关键代码,会出现什么样的结果呢。
出现5个5,这是为什么呢,怎么和我们想的不一样,按理说不应该是出现0、1、2、3、4这样的数?
这里因为for循环是一定会比线程调用快,每一遍循环完,只是提交了线程,还没有调用,当调用时,循环已经结束,额调用时只会取最后i的值。
这就会出现5个5的情况,那么如何才能出现我们想要的结果呢。
1 2 3 4 5 6 7 8 9 10 | for ( int i = 0; i < 5; i++) { int k = i; new Action(() => { Thread.Sleep(100); // Console.WriteLine(i); Console.WriteLine(k); }).BeginInvoke( null , null ); } |
只需要在循环体中加一个变量存储i的值,就可以了。
4、线程安全 lock
关于线程安全,有的人太过于重视,而也有的人一点也不关心。那么我们应该怎么做线程安全呢。
1 2 3 4 5 6 7 8 9 10 11 | private int TotalCount = 0; <br> for ( int i = 0; i < 10000; i++) { taskList.Add(taskFactory.StartNew(() => { this .TotalCount += 1; })); } Task.WaitAll(taskList.ToArray()); Console.WriteLine( this .TotalCount); |
先来看看这段代码,可能大多数人会认为结果会是10000这样的结果,但是事实呢
你会发现,第一次是9997,第二次9998,第三次是9996,没有一次出现我们想要的结果。这又是为什么,
因为我们声明的private int TotalCount = 0,是共有变量,所有的线程都是调用同一个,这就出现了线程安全的问题。
那么我们应该如何解决这种情况呢,这就要加一把锁。
private static object btnThreadCore_Click_Lock = new object();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <em id= "__mceDel" > for ( int i = 0; i < 10000; i++) { taskList.Add(taskFactory.StartNew(() => { lock (btnThreadCore_Click_Lock) { this .TotalCount += 1; } })); } Task.WaitAll(taskList.ToArray()); Console.WriteLine( this .TotalCount); </em> |
这样在执行相加的时候只会允许一个线程进行相加。
讲完这四点,再来说说Await/Async,这两个一般都是同时出现
1、只出现Async,会出现一个警告,合普通线程没什么区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private static async void NoReturnNoAwait() { //主线程执行 Console.WriteLine( "NoReturnNoAwait Sleep before Task,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); Task task = Task.Run(() => //启动新线程完成任务 { Console.WriteLine( "NoReturnNoAwait Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Console.WriteLine( "NoReturnNoAwait Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); }); //主线程执行 Console.WriteLine( "NoReturnNoAwait Sleep after Task,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); } |
同时出现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private static async void NoReturn() { //主线程执行 Console.WriteLine( "NoReturn Sleep before await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); TaskFactory taskFactory = new TaskFactory(); Task task = taskFactory.StartNew(() => { Console.WriteLine( "NoReturn Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Console.WriteLine( "NoReturn Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); }); await task; //子线程执行 其实是封装成委托,在task之后成为回调(编译器功能 状态机实现) Console.WriteLine( "NoReturn Sleep after await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); } |
带有await时,后面执行时,会发现也是子线程在执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private static async Task NoReturnTask() { //这里还是主线程的id Console.WriteLine( "NoReturnTask Sleep before await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); Task task = Task.Run(() => { Console.WriteLine( "NoReturnTask Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Console.WriteLine( "NoReturnTask Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); }); await task; Console.WriteLine( "NoReturnTask Sleep after await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); //return new TaskFactory().StartNew(() => { }); 不能return } |
这种和前面的没什么区别,只不过它带有返回值Task。其运行结果是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private static async Task< long > SumAsync() { Console.WriteLine( "SumAsync {1} start ManagedThreadId={0}" , Thread.CurrentThread.ManagedThreadId, 111); long result = 0; await Task.Run(() => { for ( int k = 0; k < 10; k++) { Console.WriteLine( "SumAsync {1} await Task.Run ManagedThreadId={0}" , Thread.CurrentThread.ManagedThreadId, k); System.Threading.Thread.Sleep(1000); } for ( long i = 0; i < 999999999; i++) { result += i; } }); Console.WriteLine( "SumAsync {1} end ManagedThreadId={0}" , Thread.CurrentThread.ManagedThreadId, 111); return result; } |
如果是带有返回值的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private static async Task< long > SumAsync() { Console.WriteLine( "SumAsync {1} start ManagedThreadId={0}" , Thread.CurrentThread.ManagedThreadId, 111); long result = 0; await Task.Run(() => { for ( long i = 0; i < 999999999; i++) { result += i; } }); Console.WriteLine( "SumAsync {1} end ManagedThreadId={0}" , Thread.CurrentThread.ManagedThreadId, 111); return result; } |
1 2 3 4 5 | Task< long > t = SumAsync(); long lResult = t.Result; //访问result 主线程等待Task的完成 //t.Wait();//等价于上一行 Console.WriteLine(lResult); |
转-https://www.cnblogs.com/xima/p/7285960.html