好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

TPL中Task执行的内联性线程重入

TPL中Task执行的内联性线程重入

TPL中Task执行的内联性线程重入

在没有TPL(Task Parallel Library)之前,使用ThreadPool处理多线程事务及等待,可能类似如下代码:

  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会为每个应用程序域维护FIFO的先入先出队列,每当调用QueueUserWorkItem时,线程池会将给定的任务放入队列中,等到有下一个可用线程时,从队列中取出执行。
所以执行的解决过发现每个任务都执行在不同的线程上。

当.NET Framework 4提供TPL库之后,我们可以通过另一种写法来完成同样的任务。

  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    }

通常,我们会看到的结果,嵌套的Task与外部调用及等待的Task使用了相同的线程池线程。

如果机器够快的话,基本上所有Task都在同一个线程上执行。

 

TPL中使用TaskScheduler来调度Task的执行,而TaskScheduler有一个特性名为“Task Inlining”。
当外部ThreadPool线程正在阻塞并等待嵌套的NestedTask完成时,NestedTask有可能在该等待的线程上执行。
这样做的优点是可以提高性能,因为节省并重用了被阻塞的线程。

在使用可能碰到的问题:
如果使用ThreadStatic标记某变量,则使该变量为每线程TLS独立存储,同时也意图该变量始终在同一线程中共享。
但在所示例子中,如果在父Task及执行线程中指定了变量的值,而子Task及相同执行线程则使用了相同的变量值, 则在某种需求下会产生问题。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于TPL中Task执行的内联性线程重入的详细内容...

  阅读:38次