线程安全是因为线程是异步的可能会同事处理多条数据但是多条数据有可能会同事执行从而导致数据的不正确性和错误
现在我们主要的来看看例子
1 int iSync = 0; 2 int iASync = 0; 3 4 List<int> iAsynclist = new List<int>(); 5 List<int> iAsyncLocklist = new List<int>(); 6 7 8 for (int i = 0; i < 10000; i++) 9 { 10 iSync++; 11 } 12 for (int i = 0; i < 10000; i++) 13 { 14 Task.Run(() => { 15 iASync++; 16 }); 17 } 18 for (int i = 0; i < 10000; i++) 19 { 20 int k = i; 21 Task.Run(() => { 22 lock (From_lock)//任意时刻只能有一个线程进入 23 { 24 iAsyncLocklist.Add(k); 25 } 26 27 }); 28 Task.Run(() => { 29 iAsynclist.Add(k); 30 }); 31 } 32 Thread.Sleep(1000*3); 33 Console.WriteLine($"同步方法是,{iSync},--异步方法为{iASync}--list异步方法{iAsynclist.Count()}--list异步方法添加Lock{iAsyncLocklist.Count()}");
这个时候我们发现使用异步的方法很容易是线程中的计数不正确也没有规律因为这些诸多的问题现在我们就要来处理一下,有关于线程完全的问题
第一个问题我们主要来解决一下
在下面的代码中我们将name13,name18手动的触发了异常 在多线程的程序当中当一个线程出现了问题我们的程序是不会直接弹黄或者其他的因为在这个程序弹黄的时候其他的程序也是会继续进行的所以我们如何接受到我们多线程的异常情况呢?
多数情况下我们是会将线程包含在Try catch 中将线程中的问题收纳到catch在逐一的展示其中 AggregateException 是专门用来处理线程的问题的其中可以收纳很多的线程信息
1 #region 多线程异常处理 2 { 3 Console.WriteLine("多线程异常处理开始"); 4 List<Task> tasklist = new List<Task>(); 5 try 6 { 7 for (int i = 0; i < 100; i++) 8 { 9 string name = $"name{i}"; 10 tasklist.Add(Task.Run(() => 11 { 12 if ("name13".Equals(name)) 13 { 14 //Console.WriteLine("name13异常"); 15 throw new Exception("name13异常"); 16 17 } 18 else if ("name18".Equals(name)) 19 { 20 //Console.WriteLine("name18异常"); 21 throw new Exception("name18异常"); 22 } 23 Console.WriteLine($"This is {name} 成功 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); 24 25 })); 26 }; 27 Task.WaitAll(tasklist.ToArray());//1,使用waitAll等待所有的线程完成 28 } 29 //两个catch 是可以的第一个详细第二个具体 30 //多线程出现异常会终结当前线程,但不会印象其他的线程 31 // 异常会被吞掉 32 //获取异常 33 catch (AggregateException aex)//多线程异常 34 { 35 foreach (var exception in aex.InnerExceptions) 36 { 37 Console.WriteLine(exception.Message); 38 } 39 } 40 catch (Exception EX) 41 { 42 Console.WriteLine(EX.Message); 43 throw new Exception(EX.Message); 44 } 45 46 Console.WriteLine("多线程异常处理完成"); 47 //线程异常中经常是需要通知别的线程不是等着WaitAll问题是需要线程取消 48 //工作中常规建议:多线程 49 50 51 } 52 #endregion
1,多线程并发任务,某个失败后,希望通知别的线程,都停下来,how?
2,Thread.Abort--终止线程; 向线程跑出异常;线程数据OS资源,可能不会立即停下来 无法掌握,
3,Task 不能外部终止(因为线程是一个计算机资源程序无法控制只能跑出异常) 只能自己才能终止自己给出一个bool
4,线程取消只能是自己内部自行取消
5,线程取消有先声明一个 CancellationTokenSource这是一个
6,CancellationTokenSource 有一个布尔属性默认为false,但是我们调用了Cancel()方法之后IsCancellationRequested将设置为true 与普通的bool类型区别,就是他只能修改一次,不能转换
1 #region 线程取消 2 { 3 //多线程并发任务,某个失败后,希望通知别的线程,都停下来,how? 4 //Thread.Abort--终止线程; 向线程跑出异常;线程数据OS资源,可能不会立即停下来 无法掌握, 5 //Task 不能外部终止(因为线程是一个计算机资源程序无法控制只能跑出异常) 只能自己才能终止自己给出一个bool 6 //线程取消只能是自己内部自行取消 7 //线程取消有先声明一个 CancellationTokenSource这是一个 8 //CancellationTokenSource 有一个布尔属性默认为false,但是我们调用了Cancel()方法之后IsCancellationRequested将设置为出 9 10 try 11 { 12 CancellationTokenSource cts = new CancellationTokenSource();//bool值 13 List<Task> tasklist = new List<Task>(); 14 for (int i = 0; i < 30; i++) 15 { 16 string name = $"name{i}"; 17 if (!cts.IsCancellationRequested) 18 Console.WriteLine($"name{i}开始"); 19 tasklist.Add(Task.Run(() => 20 { 21 try 22 { 23 Thread.Sleep(new Random().Next(50, 100)); 24 if ("name13".Equals(name)) 25 { 26 throw new Exception("name13异常"); 27 } 28 else if ("name18".Equals(name)) 29 { 30 throw new Exception("name18异常"); 31 } 32 if (!cts.IsCancellationRequested) 33 { 34 Console.WriteLine($"This is {name} 结束 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); 35 } 36 else 37 { 38 Console.WriteLine($"This is {name} 取消 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}"); 39 } 40 } 41 catch (Exception ex) 42 { 43 Console.WriteLine(ex.Message); 44 cts.Cancel(); 45 } 46 47 }, cts.Token)); 48 }; 49 // 通知其他线程索准备的步骤 50 //1, 准备cts CancellationTokenSource cts = new CancellationTokenSource();//bool值 51 //2,try-cath -canncel 52 //3,要Action要随时判断IsCancellationRequested 53 //只能尽快停止肯定是有延迟的,判断环节才会结束 54 55 Task.WaitAll(tasklist.ToArray()); 56 //1,启动线程传递Token, 57 //2,异常抓取 58 //在Cannel时还没有启动的任务,就不启动了;也是跑异常;原理cts.Token.ThrowIfCancellationRequested抛出异常 59 60 } 61 catch (AggregateException aex)//多线程异常 62 { 63 64 foreach (var exception in aex.InnerExceptions) 65 { 66 Console.WriteLine(exception.Message); 67 } 68 } 69 catch (Exception ex) 70 { 71 Console.WriteLine(ex.Message); 72 } 73 74 } 75 76 #endregion
cts.Token 还一种回调
线程变量是一种程序中的闭包情况 下面的代码中第一种情况中我们是直接将i 赋值给了线程中的i直接赋值 但是这样我们会发现 会一直输出i 等于5 那是因为在执行线程的时候i早已经变里了5次,i一直是5
而第二种情况是i赋值给了k 而每一次的k都是不一样的所以,k是会从0开始的到10 依次执行
1 #region 临时变量 2 { 3 4 #region 第一种情况 5 for (int i = 0; i < 10; i++) 6 { 7 Task.Run(() => 8 { 9 Console.WriteLine($" this is i {i}线程Id为{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); 10 }); 11 } 12 #endregion 13 14 15 #region 第二种情况 16 //临时变量问题,线城市非阻塞的.延迟启动是,线程执行已经执行完毕 17 //K 是闭包里面的变量,每次都是一个新的K值 18 for (int i = 0; i < 10; i++) 19 { 20 int k = i; 21 Task.Run(() => { 22 Console.WriteLine($" this is i-- {i} K--{k}线程Id为{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); 23 }); 24 } 25 #endregion 26 } 27 #endregion
1 #region 线程安全问题 2 // 线程安全问题如果你的代码在进程中有多个线程同事运行同一段,如果每次运行的结果都更单线程运行时结果一次,那么就是线程安全 3 4 //线程安全问题一般都是全局变量/共享资源/静态变量/硬盘文件/数据库的值/只要是多线程都能访问和修改的值 5 6 //发生是因为多个线程相同的操作,出现了覆盖,怎么解决 7 // 1,lock() 8 // lock 是一个语法糖他是占用了一个引用,别的线程只能等着 9 //lock 必须是引用类型 10 //推荐锁 private static readonly object,不能是null会跑异常,也不能是string会出现共享因为如果名称一样是,路径地址是一样的在路劲地址上会出现冲突 11 //lock中的代码不要太多,因为这是单线程的 12 13 //线程安全集合 14 //System.Collections.Concurrent.ConcurrentQueue 控制原子性 15 16 //将数据分拆 17 18 19 Program program = new Program(); 20 Test tset = new Test(); 21 //Task.Delay(1000).ContinueWith(x => 22 //{ 23 // lock (tset) 24 // { 25 // Console.WriteLine("*****kasih*******"); 26 // Thread.Sleep(5000); 27 // Console.WriteLine("*****k结束*******"); 28 29 // } 30 //}); 31 // tset.DoTest(); 32 33 34 int iSync = 0; 35 int iASync = 0; 36 37 List<int> iAsynclist = new List<int>(); 38 List<int> iAsyncLocklist = new List<int>(); 39 40 41 for (int i = 0; i < 100_000; i++) 42 { 43 iSync++; 44 } 45 for (int i = 0; i < 100_000; i++) 46 { 47 Task.Run(() => { 48 iASync++; 49 }); 50 } 51 for (int i = 0; i < 100_000; i++) 52 { 53 int k = i; 54 Task.Run(() => { 55 lock (From_lock)//任意时刻只能有一个线程进入 56 { 57 iAsyncLocklist.Add(k); 58 } 59 60 }); 61 Task.Run(() => { 62 iAsynclist.Add(k); 63 }); 64 } 65 Thread.Sleep(1000*3); 66 Console.WriteLine($"同步方法是,{iSync},--异步方法为{iASync}--list异步方法{iAsynclist.Count()}--list异步方法添加Lock{iAsyncLocklist.Count()}"); 67 68 #endregion 69 70 71 Console.Read(); 72 } 73 private static readonly object From_lock = new object(); 74 75
原文:https://www.cnblogs.com/YZM97/p/11903834.html