菜鳥(niǎo)學(xué)習(xí)并行編程,參考《C#并行編程高級(jí)教程.PDF》,如有錯(cuò)誤,歡迎指正。
C#并行編程-相關(guān)概念
C#并行編程-Parallel
C#并行編程-Task
C#并行編程-并發(fā)集合
C#并行編程-線程同步原語(yǔ)
C#并行編程-PLINQ:聲明式數(shù)據(jù)并行
任務(wù)簡(jiǎn)介
TPL引入新的基于任務(wù)的編程模型,通過(guò)這種編程模型可以發(fā)揮多核的功效,提升應(yīng)用程序的性能,不需要編寫(xiě)底層復(fù)雜且重量級(jí)的線程代碼。
但需要注意:任務(wù)并不是線程(任務(wù)運(yùn)行的時(shí)候需要使用線程,但并不是說(shuō)任務(wù)取代了線程,任務(wù)代碼是使用底層的線程(軟件線程,調(diào)度在特定的硬件線程或邏輯內(nèi)核上)運(yùn)行的,任務(wù)與線程之間并沒(méi)有一對(duì)一的關(guān)系。)
創(chuàng)建一個(gè)新的任務(wù)時(shí),調(diào)度器(調(diào)度器依賴于底層的線程池引擎)會(huì)使用工作竊取隊(duì)列找到一個(gè)最合適的線程,然后將任務(wù)加入隊(duì)列,任務(wù)所包含的代碼會(huì)在一個(gè)線程中運(yùn)行。如圖:

System.Threading.Tasks.Task
一個(gè)Task表示一個(gè)異步操作,Task提供了很多方法和屬性,通過(guò)這些方法和屬性能夠?qū)ask的執(zhí)行進(jìn)行控制,并且能夠獲得其狀態(tài)信息。
Task的創(chuàng)建和執(zhí)行都是獨(dú)立的,因此可以對(duì)關(guān)聯(lián)操作的執(zhí)行擁有完全的控制權(quán)。
使用Parallel.For、Parallel.ForEach的循環(huán)迭代的并行執(zhí)行,TPL會(huì)在后臺(tái)創(chuàng)建System.Threading.Tasks.Task的實(shí)例。
使用Parallel.Invoke時(shí),TPL也會(huì)創(chuàng)建與調(diào)用的委托數(shù)目一致的System.Threading.Tasks.Task的實(shí)例。
注意項(xiàng)
程序中添加很多異步的操作作為Task實(shí)例加載的時(shí)候,為了充分利用運(yùn)行時(shí)所有可用的邏輯內(nèi)核,任務(wù)調(diào)度器會(huì)嘗試的并行的運(yùn)行這些任務(wù),也會(huì)嘗試在所有的可用內(nèi)核上對(duì)工作進(jìn)行負(fù)載均衡。
但在實(shí)際的編碼過(guò)程當(dāng)中,并不是所有的代碼片段都能夠方便的用任務(wù)來(lái)運(yùn)行,因?yàn)槿蝿?wù)會(huì)帶來(lái)額外的開(kāi)銷,盡管這種開(kāi)銷比添加線程所帶來(lái)的開(kāi)銷要小,但是仍然需要將這個(gè)開(kāi)銷考慮在內(nèi)。
Task狀態(tài)與生命周期
一個(gè)Task實(shí)例只會(huì)完成其生命周期一次,當(dāng)Task到達(dá)它的3種肯呢過(guò)的最終狀態(tài)之一是,就無(wú)法回到之前的任何狀態(tài)



下面貼代碼,詳解見(jiàn)注釋,方便大家理解Task的狀態(tài):

class PRogram { /* coder:釋迦苦僧 */ static void Main(string[] args) { /* 創(chuàng)建一個(gè)任務(wù) 不調(diào)用 不執(zhí)行 狀態(tài)為Created */ Task tk = new Task(() => { }); Console.WriteLine(tk.Status.ToString()); /* 創(chuàng)建一個(gè)任務(wù) 執(zhí)行 狀態(tài)為 WaitingToRun */ Task tk1 = new Task(() => { }); tk1.Start();/*對(duì)于安排好的任務(wù),就算調(diào)用Start方法也不會(huì)立馬啟動(dòng) 此時(shí)任務(wù)的狀態(tài)為WaitingToRun*/ Console.WriteLine(tk1.Status.ToString()); /* 創(chuàng)建一個(gè)主任務(wù) */ Task mainTask = new Task(() => { SpinWait.SpinUntil(() => { return false; }, 30000); }); /* 將子任務(wù)加入到主任務(wù)完成之后執(zhí)行 */ Task subTask = mainTask.ContinueWith((t1) => { }); /* 啟動(dòng)主任務(wù) */ mainTask.Start(); /* 此時(shí)子任務(wù)狀態(tài)為 WaitingForActivation */ Console.WriteLine(subTask.Status.ToString()); /* 創(chuàng)建一個(gè)任務(wù) 執(zhí)行 后 等待一段時(shí)間 并行未結(jié)束的情況下 狀態(tài)為 Running */ Task tk2 = new Task(() => { SpinWait.SpinUntil(() => false, 30000); }); tk2.Start();/*對(duì)于安排好的任務(wù),就算調(diào)用Start方法也不會(huì)立馬啟動(dòng)*/ SpinWait.SpinUntil(() => false, 300); Console.WriteLine(tk2.Status.ToString()); /* 創(chuàng)建一個(gè)任務(wù) 然后取消該任務(wù) 狀態(tài)為Canceled */ CancellationTokenSource cts = new CancellationTokenSource(); Task tk3 = new Task(() => { for (int i = 0; i < int.MaxValue; i++) { if (!cts.Token.IsCancellationRequested) { cts.Token.ThrowIfCancellationRequested(); } } }, cts.Token); tk3.Start();/*啟動(dòng)任務(wù)*/ SpinWait.SpinUntil(() => false, 100); cts.Cancel();/*取消該任務(wù)執(zhí)行 但并非立馬取消 所以對(duì)于Canceled狀態(tài)也不會(huì)立馬生效*/ SpinWait.SpinUntil(() => false, 1000); Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled); SpinWait.SpinUntil(() => false, 1000); Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled); SpinWait.SpinUntil(() => false, 1000); Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled); /*創(chuàng)建一個(gè)任務(wù) 讓它成功的運(yùn)行完成 會(huì)得到 RanToCompletion 狀態(tài)*/ Task tk4 = new Task(() => { SpinWait.SpinUntil(() => false, 10); }); tk4.Start(); SpinWait.SpinUntil(() => false, 300); Console.WriteLine(tk4.Status.ToString()); /*創(chuàng)建一個(gè)任務(wù) 讓它運(yùn)行失敗 會(huì)得到 Faulted 狀態(tài)*/ Task tk5 = new Task(() => { throw new Exception(); }); tk5.Start(); SpinWait.SpinUntil(() => false, 300); Console.WriteLine(tk5.Status.ToString()); Console.ReadLine(); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }View Code

使用任務(wù)來(lái)對(duì)代碼進(jìn)行并行化
使用Parallel.Invoke可以并行加載多個(gè)方法,使用Task實(shí)例也能完成同樣的工作,下面貼代碼:

class Program { private static ConcurrentQueue<Product> queue = null; /* coder:釋迦苦僧 */ static void Main(string[] args) { queue = new ConcurrentQueue<Product>(); Task tk1 = new Task(() => { SetProduct(1); SetProduct(3);}); Task tk2 = new Task(() => SetProduct(2)); tk1.Start(); tk2.Start(); Console.ReadLine(); } static void SetProduct(int index) { Parallel.For(0, 10000, (i) => { Product model = new Product(); model.Name = "Name" + i; model.SellPrice = i; model.Category = "Category" + i; queue.Enqueue(model); }); Console.WriteLine("SetProduct {0} 執(zhí)行完成", index); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }View Code
等待任務(wù)完成Task.WaitAllTask.WaitAll 方法,這個(gè)方法是同步執(zhí)行的,在Task作為參數(shù)被接受,所有Task結(jié)束其執(zhí)行前,主線程不會(huì)繼續(xù)執(zhí)行下一條指令,下面貼代碼

class Program { private static ConcurrentQueue<Product> queue = null; /* coder:釋迦苦僧 */ static void Main(string[] args) { queue = new ConcurrentQueue<Product>(); Task tk1 = new Task(() => { SetProduct(1); SetProduct(3); }); Task tk2 = new Task(() => SetProduct(2)); tk1.Start(); tk2.Start(); /*等待任務(wù)執(zhí)行完成后再輸出 ====== */ Task.WaitAll(tk1, tk2); Console.WriteLine("等待任務(wù)執(zhí)行完成后再輸出 ======"); Task tk3 = new Task(() => { SetProduct(1); SetProd
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注