1、下面for循环20个线程,到11,12号的时候执行失败,这里我也用了try catch来捕获异常。
private void button11_Click(object sender, EventArgs e) { TaskFactory taskFactory = new TaskFactory(); ListtaskList = new List (); try { for (int i = 0; i < 20; i++) { string name = string.Format($"Click_{i}"); Action
打印出来发现并没有捕获到异常
那么我再新增一句:
Task.WaitAll(taskList.ToArray());
这样我们就可以成功捕获到异常了。 同时,我们也可以通过AggregateException,捕获到我们异常的数据。
最开始我们抓不到异常,是因为系统跑出了try catch,我们抓不到。
而WaitAll可以抓到多线程里面的所有异常
但是产生了一个新的问题,WaitAll会卡界面
线程里面的action不允许出现异常,需要自己处理好
多个线程并发,某个失败后,希望别的线程停下来。
task外部无法中止,Thread.Abort不靠谱,因为线程是OS的资源,无法掌控啥时候取消
线程自己停止自己——公共的访问变量——修改它——线程不断的检测它(会有延迟)
CancellationTokenSource标志任务是否取消 Cancel 表示取消 IsCancellationRequested表示是否取消。
Token启动Task的时候传入,那么如果Cancel 了,这个任务就会放弃启动,抛出一个异常
private void button12_Click(object sender, EventArgs e) { TaskFactory taskFactory = new TaskFactory(); ListtaskList = new List (); CancellationTokenSource cts = new CancellationTokenSource(); //bool值 for (int i = 0; i < 20; i++) { string name = $"Click_{i}"; Action
1、闭包问题
private void button13_Click(object sender, EventArgs e) { for (int i = 0; i < 5; i++) { int k = i; Task.Run(() => { Thread.Sleep(100); Console.WriteLine(k); }); } }
抛出问题:
private void button14_Click(object sender, EventArgs e) { TaskFactory taskFactory = new TaskFactory(); ListtaskList = new List (); int totalCount = 0; List intList = new List (); for (int i = 0; i < 10000; i++) { int newi = i; taskList.Add(taskFactory.StartNew(() => { totalCount += 1; intList.Add(newi); })); } Task.WaitAll(taskList.ToArray()); Console.WriteLine(totalCount); Console.WriteLine(intList.Count); }
这里我声明了一个int类型的临时变量和一个List
可以发现打印出来的数目并不一致,并且都小于10000?
这就是线程安全的问题,是多个线程同时操作同一变量导致的。
对于共有变量:都能访问的局部变量/全局变量/数据库的值/硬盘文件
我们尝试加上锁(Lock)(Lock可以算是一种语法糖)。
private static readonly object btnThreadCore_Click_Lock = new object(); private void button14_Click(object sender, EventArgs e) { TaskFactory taskFactory = new TaskFactory(); ListtaskList = new List (); int totalCount = 0; List intList = new List (); for (int i = 0; i < 10000; i++) { int newi = i; taskList.Add(taskFactory.StartNew(() => { lock (btnThreadCore_Click_Lock) { totalCount += 1; intList.Add(newi); } })); } Task.WaitAll(taskList.ToArray()); Console.WriteLine(totalCount); Console.WriteLine(intList.Count); }
发现打印出来的数量都正常。
因为lock后的方法块,任意时刻只有一个线程可以进入
介绍:Lock等同于Monitor.Enter(btnThreadCore_Click_Lock);
离开等同于调用了Monitor.Exit();
限制:Lock只能锁引用类型(占用引用链接),不要用string,因为享元
微软提供的标准写法:
private static readonly object btnThreadCore_Click_Lock = new object();
Lock 最好锁private的,防止外面也去lock
static 全场唯一 ,避免不同实例锁的不同
readonly 只读,不要改动
object 表示引用
lock(this)每次实例化都是不同的锁,同一个实例时相同的锁
但是这个实例别人也能访问到,别人也能锁定
缺点:
Lock解决,因为只有一个线程可以进去,没有并发,所以解决了问题,但是牺牲了性能,所以要尽量缩小lock的范围
最后还是需要一个线程完成操作
3、最好的方法就是不要冲突——数据拆分,避免冲突!!
上一篇:AWS云用户创建