TPL中Task执行的内联性线程重入
在沒(méi)有 TPL(Task Parallel Library)之前,使用 ThreadPool 處理多線程事務(wù)及等待,可能類(lèi)似如下代碼:
1 class Program 2 { 3 [ThreadStatic] 4 static int PerThreadValue; 5 6 static void Main(string[] args) 7 { 8 Console.WriteLine("Main thread: {0}", 9 Thread.CurrentThread.ManagedThreadId); 10 11 Console.WriteLine(); 12 13 for (int i = 1; i <= 5; i++) 14 { 15 AutoResetEvent signalOuter = new AutoResetEvent(false); 16 ThreadPool.QueueUserWorkItem((s) => 17 { 18 PerThreadValue = i; 19 Console.WriteLine("Launch thread: {0}, Value: {1}", 20 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 21 22 AutoResetEvent signalInner = new AutoResetEvent(false); 23 ThreadPool.QueueUserWorkItem((n) => 24 { 25 Console.WriteLine(" Nested thread: {0}, Value: {1}", 26 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 27 signalInner.Set(); 28 }); 29 signalInner.WaitOne(); 30 31 Console.WriteLine(" Launch back thread: {0}, Value: {1}", 32 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 33 signalOuter.Set(); 34 }); 35 signalOuter.WaitOne(); 36 } 37 38 Console.ReadKey(); 39 } 40 }ThreadPool 會(huì)為每個(gè)應(yīng)用程序域維護(hù) FIFO 的先入先出隊(duì)列,每當(dāng)調(diào)用 QueueUserWorkItem 時(shí),線程池會(huì)將給定的任務(wù)放入隊(duì)列中,等到有下一個(gè)可用線程時(shí),從隊(duì)列中取出執(zhí)行。
所以執(zhí)行的解決過(guò)發(fā)現(xiàn)每個(gè)任務(wù)都執(zhí)行在不同的線程上。
當(dāng) .NET Framework 4 提供 TPL 庫(kù)之后,我們可以通過(guò)另一種寫(xiě)法來(lái)完成同樣的任務(wù)。
1 class Program 2 { 3 [ThreadStatic] 4 static int PerThreadValue; 5 6 static void Main(string[] args) 7 { 8 Console.WriteLine("Main thread: {0}", 9 Thread.CurrentThread.ManagedThreadId); 10 11 Console.WriteLine(); 12 13 for (int i = 1; i <= 5; i++) 14 { 15 Task.Factory.StartNew( 16 () => 17 { 18 PerThreadValue = i; 19 Console.WriteLine("Launch thread: {0}, Value: {1}", 20 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 21 22 Task.Factory.StartNew( 23 () => 24 { 25 Console.WriteLine(" Nested thread: {0}, Value: {1}", 26 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 27 }).Wait(); 28 29 Console.WriteLine(" Launch back thread: {0}, Value: {1}", 30 Thread.CurrentThread.ManagedThreadId, PerThreadValue); 31 }).Wait(); 32 } 33 34 Console.ReadKey(); 35 } 36 }通常,我們會(huì)看到的結(jié)果,嵌套的 Task 與外部調(diào)用及等待的 Task 使用了相同的線程池線程。
如果機(jī)器夠快的話(huà),基本上所有 Task 都在同一個(gè)線程上執(zhí)行。
?
TPL 中使用 TaskScheduler 來(lái)調(diào)度 Task的 執(zhí)行,而 TaskScheduler 有一個(gè)特性名為 “Task Inlining”。
當(dāng)外部 ThreadPool 線程正在阻塞并等待嵌套的 NestedTas k完成時(shí),NestedTask 有可能在該等待的線程上執(zhí)行。
這樣做的優(yōu)點(diǎn)是可以提高性能,因?yàn)楣?jié)省并重用了被阻塞的線程。
在使用可能碰到的問(wèn)題:
如果使用 ThreadStatic 標(biāo)記某變量,則使該變量為每線程 TLS 獨(dú)立存儲(chǔ),同時(shí)也意圖該變量始終在同一線程中共享。
但在所示例子中,如果在父 Task 及執(zhí)行線程中指定了變量的值,而子 Task 及相同執(zhí)行線程則使用了相同的變量值, 則在某種需求下會(huì)產(chǎn)生問(wèn)題。
本文轉(zhuǎn)自匠心十年博客園博客,原文鏈接:http://www.cnblogs.com/gaochundong/archive/2013/04/19/tpl_task_inlining.html,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的TPL中Task执行的内联性线程重入的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SQL*Loader 笔记 (一) 热身
- 下一篇: HashMap原理总结