异步多线程的异常,抓不到,因为是在子线程执行。
#region 多线程的异常处理、线程取消、临时变量、lock { try { List<Task> list = new List<Task>(); TaskFactory taskFactory = new TaskFactory(); //取消线程 CancellationTokenSource cts = new CancellationTokenSource(); for (int i = 0; i < 5; i++) { string name = "task" + i; //无参数,无返回值的一个委托 Action act = new Action(() => { try { if (name == "task4") { throw new Exception("抛出异常信息"); } Thread.Sleep(2000); //检查Task是否被取消了 if (cts.IsCancellationRequested) { Console.WriteLine(name + " 被取消:" + cts.IsCancellationRequested); } else { Console.WriteLine(name + " 执行成功"); } } catch (Exception ex) { cts.Cancel();//取消,所有子线程都会被取消 Console.WriteLine("子线程异常:{0}", ex.Message); } }); Task task = taskFactory.StartNew(act, cts.Token); list.Add(task); //cts.Token是一个标识,用来标记线程是否已经被取消了 //输出结果 //子线程异常:抛出异常信息 //task2 被取消:True //task1 被取消:True //task3 被取消:True //task0 被取消:True } Task.WaitAll(list.ToArray()); } catch (AggregateException ex) { foreach (var item in ex.InnerExceptions) { Console.WriteLine(item.Message); } } } #endregion #region 多线程延迟问题 { Action<int> act = new Action<int>(t => { Thread.Sleep(200); Console.WriteLine(t); }); for (int i = 0; i < 10; i++) { //Task.Run 需要接受 无参数、无返回值 的Action,所以需要包装一下 //会出现错误,输出的全部是 10,因为Task.Run去申请线程后并没有立即执行,当执行的时候for循环已经结束,i已经变成10了 //Task.Run(() => act(i)); //解决方法,每次循环都会创建一个新的变量k,执行的时候引用的是当时的k int k = i; Task.Run(() => act(k)); } } #endregion
线程变量副本ThreadLocal/ThreadStatic
static ThreadLocal<string> threadLocal = new ThreadLocal<string>(); static void Main(string[] args) { List<Task> list = new List<Task>(); for (int i = 0; i < 4; i++) { Task task = Task.Run(() =>{ Thread.Sleep(2000); threadLocal.Value = "线程" + Thread.CurrentThread.ManagedThreadId; PrintLocalData(); }); list.Add(task); } Task.WaitAll(list.ToArray()); } public static void PrintLocalData() { Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, threadLocal.Value); }
结果:5:线程5
8:线程8
9:线程9
11:线程11
ThreadLocal的源码中很简单,就是使用了ThreadStatic特性:[ThreadStatic]使每个线程都有一个变量副本。
所以,上面的static ThreadLocal<string> threadLocal = new ThreadLocal<string>(); 可以替换为:
[ThreadStatic]
static string localValue;
注:[ThreadStatic]依赖当前线程,当线程池的线程被重用时,可能该字段又表现为“公用”的了。及时清空可以一定程度上解决该问题。