多線程是一個不會過時的話題,因為每個開發(fā)的成長必然要掌握這個知識點,否則半懂不懂怎么保證系統(tǒng)的可靠性和性能,其實在網(wǎng)上隨便一搜都會有海量的文章說這個話題,大多數(shù)寫得很細(xì)寫得非常好,但發(fā)現(xiàn)很少有概覽性的文章,我希望能借本文給大家一個全局視野,結(jié)合多年實踐幫大家快速學(xué)習(xí)或者回顧思考,對感興趣的知識點再深入學(xué)習(xí)了解。
一、知識點概括
二、具體實例演示如何實現(xiàn)
> Thread?多線程最基礎(chǔ)的類
//ThreadPool.GetMaxThreads(out int maxTCount, out int maxPCount); //
ThreadPool.GetMinThreads(out int minTCount, out int minPCount);//
ThreadPool.SetMaxThreads(maxTCount, maxPCount);// 調(diào)整最大線程數(shù) //
ThreadPool.SetMinThreads(minTCount, minPCount);// 調(diào)整最小線程數(shù) long tick =
C_ITEM_COUNT; ManualResetEvent signal= new ManualResetEvent(false);
Console.WriteLine("========== 示例:采用Thread執(zhí)行處理 =========="); for (int i = 0; i <
C_ITEM_COUNT; i++) { new Thread((obj) => { Thread.Sleep(500); Console.Write("
{0}", obj); if (Interlocked.Decrement(ref tick) == 0) signal.Set();
}).Start(i); } Console.Write(" 等待子線程執(zhí)行 "); signal.WaitOne();
Console.WriteLine(); Console.WriteLine("全部線程執(zhí)行完畢,按任意鍵繼續(xù)...");
> ThreadPool?線程池,XX池的概念可以廣泛應(yīng)用于其他資源管理,例如字體池(防句柄泄露)、短信貓池等等
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("
========== 示例:采用ThreadPool執(zhí)行處理 =========="); for (int i = 0; i < C_ITEM_COUNT;
i++) { ThreadPool.QueueUserWorkItem((obj) => { Thread.Sleep(500); Console.Write(
" {0} ", obj); if (Interlocked.Decrement(ref tick) == 0) signal.Set(); }, i); }
Console.Write(" 等待子線程執(zhí)行 "); signal.WaitOne(); Console.WriteLine();
Console.WriteLine("全部線程執(zhí)行完畢,按任意鍵繼續(xù)...");
> Task?任務(wù),功能豐富用法靈活。結(jié)合現(xiàn)實生活用字面意思去理解就好:可以同時做多個任務(wù),任務(wù)做完可以接著做其他任務(wù),任務(wù)可能會取消等等。
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("
========== 示例:采用Task執(zhí)行處理,注意取消了處理{0}的進(jìn)程 ==========", C_ITEM_COUNT - 2); var
tasks =new Tuple<Task, CancellationTokenSource>[C_ITEM_COUNT]; for (int i = 0;
i < C_ITEM_COUNT; i++) { var cts = new CancellationTokenSource(); var task =
Task.Factory.StartNew((obj) => { Thread.Sleep(500); Console.Write(" {0} ",
obj); }, i, cts.Token); task.ContinueWith((t)=> { if (Interlocked.Decrement(ref
tick) ==0) signal.Set(); }); tasks[i] = new Tuple<Task, CancellationTokenSource>
(task, cts); } tasks[C_ITEM_COUNT- 2].Item2.Cancel();// 取消線程。 Console.Write("
等待子線程執(zhí)行"); signal.WaitOne(); Console.WriteLine(); Console.WriteLine("
全部線程執(zhí)行完畢,按任意鍵繼續(xù)...");
> Parallel?并行
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("
========== 示例:采用Parallel執(zhí)行處理 =========="); Parallel.For(0, C_ITEM_COUNT, obj =>
{ Thread.Sleep(500); Console.Write(" {0} ", obj); if (Interlocked.Decrement(ref
tick) ==0) signal.Set(); }); Console.Write(" 等待子線程執(zhí)行 "); signal.WaitOne();
Console.WriteLine(); Console.WriteLine("全部線程執(zhí)行完畢,按任意鍵繼續(xù)...");
以上示例執(zhí)行結(jié)果如下,重點可以關(guān)注下"等待子線程執(zhí)行"這個節(jié)點,理解主線程和各子線程的優(yōu)先執(zhí)行順序
三、性能對比(理解線程池技術(shù)的性能也可以通過最大最小線程數(shù)調(diào)節(jié))
> 循環(huán)數(shù):200,線程池參數(shù):默認(rèn)
>?循環(huán)數(shù):200,線程池參數(shù):50 - 1000
>?循環(huán)數(shù)200,線程池參數(shù):100-1000
?
>?循環(huán)數(shù)200,線程池參數(shù):200-1000
?
最大線程數(shù) ~ 最小線程數(shù) Thread(ms) ThreadPool(ms) Task(ms) Parallel(ms)
2047/1000 ~ 12/12 2712.09 8057.14 8585.01 7526.57
1000/1000 ~ 50/50 2733.25 2289.96 2218.29 3660.33
1000/1000 ~ 100/100 2503.08 1620.73 1534.50 1742.78
1000/1000 ~ 200/200 2999.27 1436.24 1150.21 935.22
?四、結(jié)論
>?Thread就像脫韁的野馬,不受控制,創(chuàng)建多少就運(yùn)行多少,可能少量時效率是高了,量大的時候除了性能沒優(yōu)勢,還可能導(dǎo)致句柄泄露。
>?ThreadPool與Task類似,但Task相比效率更高用法更靈活。
>?Parallel自帶了同步功能,不需要用信號量來做額外的同步等待。
>?ThreadPool、Task、Parallel的性能都取決于線程池最大線程數(shù)和最小線程數(shù)。
>?推薦使用 Task 和 Parallel,具體用哪個可以參考用法自己斟酌。
熱門工具 換一換