zoukankan      html  css  js  c++  java
  • 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 线程正在阻塞并等待嵌套的 NestedTas k完成时,NestedTask 有可能在该等待的线程上执行。
    这样做的优点是可以提高性能,因为节省并重用了被阻塞的线程。

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

  • 相关阅读:
    MongoDB数据导入hbase + 代码
    Hbase批量插入优化记录
    net.sf.fjep.fatjar_0.0.32 eclipse4.x 可以用的jar包
    %E3%80%90%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E3%80%91
    学习《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码
    《Python生物信息学数据管理》中文PDF+英文PDF+代码
    推荐《R数据可视化手册》高清英文版PDF+中文版PDF+源代码
    学习优化《机器学习与优化》中文PDF+英文PDF
    入门python:《Python编程从入门到实践》中文PDF+英文PDF+代码学习
    推荐《SQL基础教程(第2版)》中文PDF+源代码+习题答案
  • 原文地址:https://www.cnblogs.com/gaochundong/p/tpl_task_inlining.html
Copyright © 2011-2022 走看看