概要:使用C#发起多线程任务十分简单,本文旨在汇总多线程编程的注意事项,重点不在于如何发起多线程,主要内容如下:
-
控制线程并发数量 -
界定共享资源 -
加锁并控制锁范围 -
子线程异常处理 -
未完成任务取消.
希望对小伙伴儿们有所帮助
01—控制线程并发数量
基础代码逻辑如下:
//semaphoreCount是设定的可并行运行的最大线程数量//taskCount是需要发起的线程的数量using (Semaphore semaphore = new Semaphore(semaphoreCount, semaphoreCount)){var woker = new Worker();Task[] tasks = new Task[taskCount];for (int step = 0; step < taskCount; step++){//获取一个信号量,如果所有信号量都已使用,则等待直到一个被释放semaphore.WaitOne();//获得信号量之后,才能发起子线程tasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData).ContinueWith((task) =>{//线程完成,释放信号量semaphore.Release();});}//...}
简单来说,是由于分时操作系统,多任务之间存在线程上下文切换,有兴趣的同学可以尝试一下,一次性启动2000个以上线程,查看计算机的资源耗用情况,以便有更真切的体会。
02—界定共享资源
03—加锁并控制锁范围
对于锁对象,推荐的写法如下,至于是不是要加static ,要看具体业务场景,静态变量的作用域是整个应用程序,如果有两个以上请求同时到达,那么在访问到加锁代码块时,请求也是串行执行的,普通变量的作用域是当前对象,锁范围也是在当前对象内,请求间相互不影响。
readonly object locker = new object();基础代码逻辑如下:
Task.Factory.StartNew((data) => { woker.Work(data); }, innerData).ContinueWith((task) =>{//判断线程处理状态,如果执行失败,则抛出异常if (task.Status == TaskStatus.Faulted){throw task.Exception;}});
05—未完成任务取消
基础代码逻辑如下:
//声明 CancellationTokenSourceusing (CancellationTokenSource cancellation = new CancellationTokenSource()){Task[] tasks = new Task[taskCount];for (int step = 0; step < steps; step++){semaphore.WaitOne();//注册cancellation.Tokentasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData, cancellation.Token).ContinueWith((task) =>{if (task.Status == TaskStatus.Faulted){//通知取消任务cancellation.Cancel(true);throw task.Exception;}semaphore.Release();});}}