线程安全和中间量 Lock
1 2 3 4 5 6 7 | { List< int > intlist = new List< int >(); for ( int i = 0; i < 10000; i++) { intlist.Add(i); } } //正常for循环完毕是 i=9999 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { List< int > intlist = new List< int >(); List<Task> tasklist = new List<Task>(); int k = 0; for ( int i = 0; i < 10000; i++) { Task.Run(() => { lock (obj_Lock) { intlist.Add(i); } }); } Task.WaitAll(tasklist.ToArray()); } //多线程循环完 k肯定是小于10000的 |
线程安全:一段业务逻辑,单线程执行和多线程执行后的结果如果完全一致,是线程安全的;否则就表示是线程不安全;
1.锁,加索--直接是标准锁-- - 锁的本质,是独占引用;加锁以后;反多线程-- - 不再是多线程执行了 - 可以解决线程安全问题-- - 不推荐大家使用--加锁--影响性能
a) 标准写法:
1 2 3 4 | <strong> private readonly static object obj_Lock = new object ();</strong> <br> <strong> lock (obj_Lock) { intlist.Add(i); } </strong> |
b) 锁对象--不要去锁String 锁 This
2.直接使用单线程?(分块/分区去执行,每一个区块对应一个线程) 把要操作的整块数据,做一个切分:例如有1000条数据,可以把这一万条数据分区、分块;分三块;每一块开启一个线程去执行;三个线程
每一个线程内部执行的动作是单线程--线程安全等待三个线程执行结束以后,再单线程做一个统一汇总;需要把程序加以设计才能完成;
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 38 39 40 | //分块、分区执行解决线程安全问题 { List< int > intlist1 = new List< int >(); List< int > intlist2 = new List< int >(); List< int > intlist3 = new List< int >(); int length = 10000; int Num1 = 3000; int Num2 = 6000; int Num3 = 10000; List<Task> taskList = new List<Task>(); taskList.Add(Task.Run(() => { for ( int i = 0; i < Num1; i++) { intlist1.Add(i); } })); taskList.Add(Task.Run(() => { for ( int i = Num1; i < Num2; i++) { intlist2.Add(i); } })); taskList.Add(Task.Run(() => { for ( int i = Num2; i < Num3; i++) { intlist3.Add(i); } })); Task.WaitAll(taskList.ToArray()); intlist1.AddRange(intlist2); intlist1.AddRange(intlist3); } } |
3.使用线程安全对象 看看数据结构 线程安全对象 List/Arraylist 都不是线程安全的集合--把list Arraylist 换成安全对象;
1 2 3 4 5 6 7 | BlockingCollection< int > blockinglist = new BlockingCollection< int >(); ConcurrentBag< int > conocurrentbag = new ConcurrentBag< int >(); ConcurrentDictionary< string , int > concurrentDictionary = new ConcurrentDictionary< string , int >(); ConcurrentQueue< int > concurrentQueue = new ConcurrentQueue< int >(); ConcurrentStack< int > concurrentStack = new ConcurrentStack< int >(); OrderablePartitioner< int > OrderablePartitioner = new OrderablePartitioner< int >(); Partitioner< int > partitionerArray = new Partitioner(); |
中间变量
1 2 3 4 5 6 7 | { for ( int i = 0; i< 5; i++) { Debug.WriteLine($ "ThreadID={Thread.CurrentThread.ManagedThreadId.ToString(" 00 ")}_i={i}" ); } } //每次打印的i都为5 在循环内部:如果直接变量i,当线程执行的时候:i会变成5; |
Task开启线程的时候,延迟开启--(延迟时间很短的); 开启多线程执行不会阻塞主线程;在循环的时候,不会阻塞主线程,循环很快;当有一个线程在正式的执行业务逻辑的时候;循环已经结束了;i已经变成5了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 可以另外定义个变量---变量,在每次循环的时候,赋值;循环多少次,就会有多少个k,每个线程使用的是每一次循环内部的k; 在多线程中,使用这个变量的值 { List<Task> tasks = new List<Task>(); for ( int i = 0; i < 5; i++) { int k = 0; tasks.Add(Task.Run(() => { Debug.WriteLine($ "ThreadID={Thread.CurrentThread.ManagedThreadId.ToString(" 00 ")}_i={i}__ k={ k}" ); })); } Task.WaitAll(tasks.ToArray()); }<br>注意这个变量一定要在循环内部去定义,在外部定义在循环内部赋值是不解决问题的 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现