2012-04-22 29 views
0

我想为某些同步代码添加并发性,并发现该进程中的性能问题,这很难理解。Task.WaitAll()之间的性能差距问题,并顺序执行Task.Wait

下面的代码的运行结果是:

Mission Fibonacci1Async cost 9.4195388 seconds, value 75025 
Mission Fibonacci2Async cost 0.2260129 seconds, value 75025 

唯一不同是2RD功能添加了一行等待Task.WhenAll(新任务[] {T1,T2});,使演出增加40倍。

有人可以向我解释吗?

static Task<int> Fibonacci1Async(int n) 
    { 
     return Task.Run<int>(() => Fibonacci1(n)); 
    } 

    static int Fibonacci1(int n) 
    { 
     if (n == 0) return 0; 
     else if (n == 1) return 1; 
     else 
     { 
      var t1 = Fibonacci1Async(n - 1); 
      var t2 = Fibonacci1Async(n - 2); 
      return t1.Result + t2.Result; 
     } 
    } 

    static Task<int> Fibonacci2Async(int n) 
    { 
     return Task.Run<int>(() => Fibonacci2(n)); 
    } 

    static int Fibonacci2(int n) 
    { 
     if (n == 0) return 0; 
     else if (n == 1) return 1; 
     else 
     { 
      var t1 = Fibonacci2Async(n - 1); 
      var t2 = Fibonacci2Async(n - 2); 

      Task.WaitAll(new Task[] { t1, t2 }); 
      return t1.Result + t2.Result; 
     } 
    } 

    static void Benchmark(Func<int, Task<int>> func) 
    { 
     DateTime time = DateTime.Now; 
     var task = func(25); 
     task.Wait(); 
     TimeSpan cost = DateTime.Now - time; 
     Console.WriteLine("Mission {0} cost {1} seconds value {2}", func.Method.Name, cost.TotalSeconds, task.Result); 
    } 

    static void Main(string[] args) 
    { 
     Benchmark(Fibonacci1Async); 
     Benchmark(Fibonacci2Async); 
     Console.ReadKey(); 
     return; 
    } 

回答

1

我怀疑答案与Task.Wait inlining有关。

在表达式t1.Result + t2.Result中,+运算符从左到右评估它的参数(串行)。所以它会阻止t1然后t2

我猜测在你的系统上,大部分时间t1已经开始,但不是t2。在这种情况下,Task.WaitAll可以将当前线程池任务“嵌入”t2而不是启动新任务,但+将在t1上阻塞。

这只是一个猜测;你应该使用一个分析器来找出究竟发生了什么。

我无法在我的系统上重现此操作。我总是看到两个版本大致相同,即使处理器亲和力应用于流程。

P.S.命名约定Async在这里并不适用。此代码未使用async/await - 它使用任务并行库。

+0

命名约定与await/async无关。这是一个实现细节。它指定调用者接收任务并获得非阻塞行为。 – usr 2012-04-22 13:46:08

+0

与返回类型组合的后缀'Async'意味着该方法使用基于任务的异步模式。我想你可以说这些'* Async'方法是TAP,但它们当然不是TAP实现的一个很好的例子。 – 2012-04-22 14:28:57

+0

例如,FileStream.ReadAsync内部不使用await。它使用IO完成端口。它没有错。来电者不需要知道。 – usr 2012-04-22 14:38:37