『QQ:1353814576』

C# 目前实际场景用过的几种多线程编程实例


因为工作关系 平时开发经常需网络传递大量文件 为了传输效率不可避免的要使用到多线程开发,但实际情况并发的线程数量并不是越多越好,在需要保证程序处理效率的情况下又需要将并发数量控制在一个合理的范围内 不然效率没提升反倒拖累了主机性能

目前开发的文件传输程序这块迭代了n多个版本 多线程并发控制代码也优化了好几个版本 这里总结了目前用过的几个版本方案供大家参考

以下列举下用过的几种多线程编程实例逻辑和代码

第一版 使用Thread + 数组队列数量控制

开发逻辑:新建一个队列控制 里面用一个List队列将启动的Thread 线程放里面 然后定时轮询这个数组 判断这个队列数量是否小于指定数量如果是就新启动一个线程 于此同时清理释放掉已结束的线程 这样就完成了比较基础的一版本线程并发控制

优点:逻辑比较粗暴 容易理解

缺点:如果没控制好处理逻辑,就会出现启动超出控制数量的线程或者线程清理不到导致创建不了新线程的问题 我当时出现的问题就是每次启动的线程总是会比当前设定的数量多一些

第二版 线程池 ThreadPool.QueueUserWorkItem() 来进行管理

开发逻辑: 封装的很完善 基本不需要写什么控制 对应配置好就得了

优点:代码简单 不用考虑线程控制逻辑 开箱即用 设定好固定的最大并发量后 只管往里面加线程 任务就可以了

缺点:超时处理逻辑代码比Task麻烦, 通过网络传输文件时 总会有个别线程卡住无法恢复导致程序就停止业务运作了 我是因为一直没解决掉加上业务需求每天会有非常多的线程 担心添加太多会出问题 就换了其他方式

代码例子:

//设置线程的并发运行数量 超出的会保持休眠 当有线程结束后会自行唤醒对应的休眠线程
ThreadPool.SetMaxThreads(Int32, Int32);
//往线程池中添加任务
 ThreadPool.QueueUserWorkItem(o => 线程方法1(参数));
 ThreadPool.QueueUserWorkItem(o => 线程方法2(参数));

第三版 使用Thread + System.Threading.ManualResetEvent 进行控制

开发逻辑: 每次启动线程控制一定的数量 将System.Threading.ManualResetEvent 设置为阻塞 然后WaitOne 每个线程结束后将运行数减一 当并发数为0时 将System.Threading.ManualResetEvent解除阻塞 继续下一个循环

优点:每次只会启动固定数量的线程 不用担心添加的量超出预期

缺点:线程控制逻辑有些麻烦 控制的好的话应该没啥问题

第四版 使用Task + System.Threading.ManualResetEvent 进行控制

开发逻辑:和第三版逻辑基本一致 只是Thread换成了Task 这一版也是目前用的最久的一版

优点:和第三版逻辑基本一致

缺点:和第三版逻辑基本一致

第五版 使用Task + Task.WaitAll 进行控制

开发逻辑: 每次启动固定数量的Task 放进数组队列 然后使用Task.WaitAll等待所有线程结束 进行下一轮任务即可

优点: 代码简单可控 每次只会启动固定数量的线程 不用担心添加的量超出预期 且无需多余的控制

缺点: 目前感觉还好 实际使用有同时反馈网络不稳定的情况下有卡住无法恢复的情况(个例)结合超时线程控制结束掉就好了

代码例子:

//线程队列
List<Task> tasks = new List<Task>();
 var t = Task.Run(new Action(() => {
                               //执行业务方法
}));
tasks.Add(t);

//等待所有线程队列结束
Task.WaitAll(tasks.ToArray());