概要:使用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—未完成任务取消
基础代码逻辑如下:
//声明 CancellationTokenSource
using (CancellationTokenSource cancellation = new CancellationTokenSource())
{
Task[] tasks = new Task[taskCount];
for (int step = 0; step < steps; step++)
{
semaphore.WaitOne();
//注册cancellation.Token
tasks[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();
});
}
}