2015-08-31 185 views
4

在下面的代碼中,在B方法中,代碼Trace.TraceInformation(「B-Started」);永遠不會被召喚。異步方法沒有並行運行

該方法是否應該平行運行?

using System.Collections.Generic; 
using System.Diagnostics; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static async Task A() 
     { 
      for (;;) 
      { 
      } 
     } 


     private static async Task B() 
     { 
      Trace.TraceInformation("B - Started"); 
     } 

     static void Main(string[] args) 
     { 
      var tasks = new List<Task> { A(), B() }; 
      Task.WaitAll(tasks.ToArray()); 
     } 
    } 
} 
+0

也許這是一個忙碌的等待質量。 A()是否將CPU固定在100%? – MatthewMartin

+0

我有4個處理器。一個是100%。仍然應該在並行(並行)中運行? –

+1

@FernandoSilva參見'Task.Run'。只添加'async'不會使它們平行。 – Eser

回答

9

簡短的回答

不,你寫你的兩個async方法,他們確實不是並行運行。將await Task.Yield();添加到您的第一個方法(例如循環內)允許他們這樣做,但是有更合理和直接的方法,高度取決於您實際需要的內容(在單個線程上交錯執行?多個實際並行執行線程?)。

龍答案

首先,聲明功能async本身並沒有使他們或異步運行的東西。它簡化了語法 - 閱讀更多關於這裏的概念:Asynchronous Programming with Async and Await

實際上A不是異步的,因爲它的方法體內沒有單一的await。第一次使用await的指令會像常規方法那樣同步運行。

從此,你await決定接下來會發生什麼,即剩餘的方法運行在上下文中的對象。

執行任務的情況發生在另一個線程,使用Task.Run或類似。

在這種情況下,加入await Task.Yield()是卓有成效的,因爲目前的同步上下文是null而這恰好確實導致任務調度器(應該是ThreadPoolTaskScheduler)到一個線程池線程執行剩餘instuctions - 一些環境或配置可能會導致你只有其中一個,所以事情將不會並行運行。

摘要

道德的故事是:要注意的區別兩個概念:

  • 併發和
  • 並行(這是通過使用async/await合理有效) (只有當併發任務得到預定正確的方式如果你執行它使用Task.RunThread等在這種情況下使用的是async完全不相干的反正)
1

不,所示的方法預計不會「平行運行」。

爲什麼B不會被調用 - 你必須通過基本一系列.Add調用構造任務tasks的名單 - 而且首先是結果被添加的A()。由於A方法沒有任何await它將在同一個線程上同步運行。之後,B()將被調用。

現在A永遠不會完成(它坐在無限循環中),所以真正的代碼甚至不會呼叫B

請注意,即使創建成功代碼永遠不會完成WaitAll,因爲A仍然位於無限循環中。

如果你想方法來「並行運行」,你需要或者隱含/新線程運行它們明確的(即具有Task.RunThread.Start)或I/O密集型調用讓方法來釋放線程與await

+2

這是事實,但這是一個非常不完整的答案。 – Machinarius

+0

更新....用於娛樂目的...將很快消失。 –

+0

您不應該刪除它,它會在當前表單上添加非常重要的信息。 – Machinarius

2

async改性劑是不是魔術產卵-A-線程這裏標記。它的唯一目的是讓編譯器知道一個方法可能依賴於一些異步操作(一個複雜的數據處理線程,I/O ...),所以它必須設置一個狀態機來協調這些異步產生的回調操作。

要使A在另一個線程上運行,您可以使用Task.Run來調用它,該調用將使用任務對象將調用包裝在一個新線程中,您可以等待。注意await -ing一個方法並不意味着你的代碼並行運行到A的全部本身:它會一直到你的線await這個Task對象,告訴編譯器你需要這個值表示Task對象回報。在這種情況下,await -ing Task.Run(A)將有效地讓你的程序永遠運行,等待A返回,這是永遠不會發生的事情(除非電腦出現故障)。

請記住,將方法標記爲async,但不會實際等待任何內容,只會產生編譯器警告的效果。如果您等待某些不是真正異步的東西(它會立即在調用線程上返回類似於Task.FromResult的東西),這意味着您的程序會受到運行時速度的限制。然而,這是非常輕微的。