这篇文章主要讲解 .NET
的任务并行,与数据并行不一样的是:数据并行以数据为处理单元,而任务并行,则以任务(工做)为单元安全
关于任务的理解,若是还有疑问,能够参考以前的文章【温故之.NET 异步】微信
若是咱们想要建立并行的任务,能够经过 Parallel.Invoke
来实现。它能够很方便的帮助咱们同时运行多个任务,以下异步
public static void WorkOne() {
// 任务一
}
public static void WorkTwo() {
// 任务二
}
Parallel.Invoke(WorkOne, WorkTwo);
// 咱们也能够经过 Lambda 表达式这样写
Parallel.Invoke(
() => {
// 任务一
}, () => {
// 任务二
}
);
复制代码
借助 Parallel.Invoke
,咱们只需表达想同时运行的操做,CLR
会处理全部线程调度的具体信息(包括将线程数量自动缩放至计算机上的内核数)性能
须要特别注意
TPL
在后台建立的Task
数量不必定与所提供的操做的数量相等。 由于TPL
可能会针对操做的数量进行不一样程度的优化学习
所以,对 Parallel.Invoke
,咱们能够这样理解(只是为了理解方便,不表示其内部具体实现也是这样的)优化
ParallelOptions
中的 MaxDegreeOfParallelism
属性来肯定具体数量Task.Run
的方式运行每个任务。每执行一个任务,就从“线程池”中取一个空闲的线程。若是没有多余的空闲线程,则等待这也能够理解为对其内部实现的一个猜想。若是有兴趣,可使用 .NET Refactor
看一下其源码spa
若是程序有 UI
线程,且任务的建立从 UI
线程开始,那么在使用方式上会有变化,以下代码所示线程
Task.Run(() => {
Parallel.Invoke(
() => {
// 任务一
}, () => {
// 任务二
});
});
复制代码
这对于其余的并行(如数据并行)也是同样的。
只要咱们须要从 UI
线程建立并行,就应该使用 Task.Run
来启动它们。不然,颇有可能产生死锁(通常出如今当并行代码内部须要访问 UI
的状况下,其余状况我也暂时没有遇到过)设计
若是咱们分不清当前建立并行的是 UI
线程仍是其余类型的线程。咱们能够统一使用 Task
的方式来启动它们。反正在大部分状况下,使用 Task
来启动也不会形成什么性能问题code
不过,若是咱们须要并行当即启动,或者尽快启动,使用 Task
来启动可能就不太合适,在系统工做量比较重的状况下,咱们也不清楚这个 Task
何时可以执行。
在这种场景下,咱们能够新建一个 Thread 来作这件事。由于 Thread
与 Task
不一样,Thread
不以任务为单位,当咱们调用 Thread.Start()
的时候,线程就会当即执行。而 Task
,当咱们调用 Task.Run
的时候,它须要接受 TPL
的调度(Task Scheduler
)。所以,其执行时间就不肯定了
针对建立并行,有如下建议
UI
线程仍是其余线程时,使用 Task.Run
来启动并行(如前面例子所示)Thread
的方式PC
端、Web
端、仍是 WebApi
后台,咱们使用 Task.Run
来启动并行是比较好的方式经过 Thread
方式启动并行,示例以下
Thread thread = new Thread(() => {
Parallel.Invoke(
() => {
Debug.WriteLine("Work 1");
},() => {
Debug.WriteLine("Work 2");
});
});
thread.Start();
复制代码
前面提到,在多处理器条件下,使用 Parallel
能够显著提高性能。但事物总有两面性,所以仍是有一些坑须要咱们注意
Parallel.For
和 Parallel.ForEach
以数据并行为主;Parallel.Invoke
以任务并行为主Partitioner
来手动的对源集合进行分块UI
线程上执行并行循环。也应尽可能避免在并行代码中更新 UI
,由于这有可能会产生数据损坏或死锁示例A
ManualResetEventSlim mre = new ManualResetEventSlim();
int processor = Environment.ProcessorCount;
var source = Enumerable.Range(0, processor * 100);
Parallel.ForEach(source, item => {
if (item == processor) {
mre.Set();
} else {
mre.Wait();
}
});
复制代码
对于这段代码,就可能会(可能性很是大)发生死锁。如前面【针对并行的建议】的最后一点所说,一样地,此处咱们也没法肯定 mre.Set()
与 mre.Wait()
到底谁先执行
后话
最近看了一些书籍,决定不管什么时候,凡是关注了个人朋友,都一概关注回去
源于如下一点:尊重是相互的,学习也是相互的
在此,也感谢在微信公众号、知乎、简书、掘金等内容平台关注个人朋友。欢迎关注公众号【嘿嘿的学习日记】,全部的文章,都会在公众号首发,Thank you~