這篇文章主要講解 .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~