2016-03-04 81 views
3
class Program 
{ 
    static void Main(string[] args) 
    { 
     var a = new A(); 

     var result = a.Method(); 

     Console.Read(); 
    } 
} 

class A 
{ 
    public async Task<string> Method() 
    { 
     await Task.Run(new Action(MethodA)); 
     await Task.Run(new Action(MethodB)); 

     return "done"; 
    } 

    public async void MethodA() 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      Console.WriteLine("MethodA" + i); 
      await Task.Delay(1000); 
     } 
    } 

    public async void MethodB() 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      Console.WriteLine("MethodB" + i); 
      await Task.Delay(1000); 
     } 
    } 
} 

Result Output意外的結果等待

enter image description here

我認爲這裏的期望是第一輸出MethodA1,MethodA2 ... MethodA9,然後開始輸出MethodB1 ... MethodB9。

但似乎不是。有沒有人對此有過解釋?謝謝!


編輯:

感謝您的答覆。其實我知道如何輸出正確的結果,但只是想知道爲什麼我的代碼行爲怪異,因爲我認爲它違背了我的異步/等待知識。

class A 
{ 
    public async Task<string> Method() 
    { 
     await Task.Run(new Action(MethodA)); 

     await Task.Run(async() => { 
      for (var i=0;i<10;i++) 
       await Task.Delay(100); 
      Console.WriteLine("should output before done"); 
     }); 

     return "done"; 
    } 

    public async void MethodA() 
    { 
     var returnString = string.Empty; 
     for (var i = 0; i < 10; i++) 
     { 
      await Task.Delay(200); 
     } 

     Console.WriteLine("MethodA " + Thread.CurrentThread.ManagedThreadId); 

    } 
} 

我簡化了代碼,現在它是這樣的。第一個等待快速轉移控制到下一個等待,然後第二個等待表現如我所料,最後輸出「完成」。但我不知道這裏有什麼不同。

Result Output

enter image description here

更新:千萬記住示例代碼是一個不好的做法。你可以read this post for more information

和一個朋友建議不同的是,

await Task.Run(new Action(MethodA));

不等於

await Task.Run(async() => { 
      for (var i=0;i<10;i++) 
       await Task.Delay(100); 
      Console.WriteLine("should output before done"); 
     }); 

前者以同步的方式調用「了methodA」,而一個海報是一個正確的異步調用。但它仍然讓我困惑,因爲如果這是真的,那麼異步操作可以並且只能用lambda表達式表示。我的意思是,沒有像'新的異步行動(...)'的權利?

更新:

我想這被認爲是相當於Task.Run(動作行爲)和東西Task.Factory.StartNew(動作動作)再次更改:

class A 
{ 
    public async Task<string> Method() 
    { 
     await Task.Factory.StartNew(new Action(MethodA)); 

     await Task.Factory.StartNew(async() => { 
      for (var i=0;i<10;i++) 
       await Task.Delay(100); 
      Console.WriteLine("should output before done"); 
     }); 

     return "done"; 
    } 

    public async void MethodA() 
    { 
     for (var i = 0; i < 10; i++) 
      await Task.Delay(200); 
     Console.WriteLine("MethodA"); 

    } 
} 

我想我需要更多的幫助...

+2

每當你發現自己輸入'async void',並且它不是用於事件處理程序的時候,你可能是在做一些錯誤的事情。 'async'應該總是返回一個'Task'。 –

回答

8

你的代碼的行爲,因爲它做的原因是因爲任務你啓動調用的方法之一,將在方法完成第一await因爲它是async void,因此只是繼續導致await Task.Run(new Action(MethodA));將控制返回到同時您的MethodA機身仍在運行。這是因爲async void基本上是火而忘了。

下面是簡化版代碼中發生的情況。

class A 
{ 
    public async Task<string> Method() 
    { 
     // You create a task to run MethodA 
     await Task.Run(new Action(MethodA)); 
     // control returns here on the first `await` in MethodA because 
     // it is `async void`. 

     return "done"; 
    } 

    public async void MethodA() 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      Console.WriteLine("MethodA" + i); 
      // Here the control is returned to the thread and because 
      // it's `async void` an `await` will only block until you reach 
      // this point the first time. 
      await Task.Delay(1000); // 
     } 
    } 
} 

爲了得到你想要的,你首先需要從方法返回一個Task因此它們可以被期待已久的結果。那麼你也不需要Task.Run

class A 
{ 
    public async Task<string> Method() 
    { 
     await MethodA(); 
     await MethodB(); 

     return "done"; 
    } 

    public async Task MethodA() 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      Console.WriteLine("MethodA" + i); 
      await Task.Delay(1000); 
     } 
    } 

    public async Task MethodB() 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      Console.WriteLine("MethodB" + i); 
      await Task.Delay(1000); 
     } 
    } 
} 
+0

感謝人,但實際上我只是想弄清楚爲什麼我的代碼似乎違背了我的異步/等待知識... –

+0

我已經添加了一些解釋,爲什麼你的代碼不按照你期望的方式工作。 – juharr

+0

我仔細閱讀了你的評論,我確實在VS中嘗試過,所有的控制轉移都像你的評論。最讓我困擾的是我在等待關鍵字之後在同一個函數中認爲進程在等待任務完成之前不會運行,這就是爲什麼等待MethodA();等待MethodB()可以按順序輸出結果的原因。但我不明白爲什麼等待Task.Run()可以做出如此大的差異。 –

0

您可能想要從MethodA和MethodB中返回任務,而不是在這些方法中寫入控制檯。像這樣:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var a = new A(); 

     var result = a.Method(); 

     Console.Read(); 
    } 
} 

class A 
{ 
    public async Task<string> Method() 
    { 
     Console.WriteLine(await MethodA()); 
     Console.WriteLine(await MethodB()); 

     return "done"; 
    } 

    public async Task<string> MethodA() 
    { 
     var returnString = string.Empty; 
     for (var i = 0; i < 10; i++) 
     { 
      returnString += "MethodA" + i + '\n'+'\r'; 
      await Task.Delay(100); 
     } 
     return returnString; 
    } 

    public async Task<string> MethodB() 
    { 
     var returnString = string.Empty; 
     for (var i = 0; i < 10; i++) 
     { 
      returnString += "MethodB" + i + '\n'+'\r'; 
      await Task.Delay(100); 
     } 
     return returnString; 
    } 
} 

注:我沒有測試過這個代碼,但它應該說明一般的想法。

-1

async + await != sync,因爲延續

詳情的Asynchronous Programming with Async and Await (C# and Visual Basic)

Async methods旨在是非阻塞操作。

An await expression in an async method doesn’t block the current thread等待執行的任務正在運行。相反,該表達式將該方法的其餘部分標記爲延續並將控制返回給方法的調用者。

+1

儘管這在技術上都是正確的,但它並不能以一種容易理解的方式真正解釋代碼的問題;特別是對於剛剛學習異步/等待的人。 –

+0

@BradleyUffner嗯..我同意。 juharr先生已經給出了相關的代碼。我只是試圖給出這個問題的清晰概念。 – SkyWalker

+1

@SkyWalker謝謝。我仔細閱讀。在同一頁面的圖片中,您可以看到DoIndependentWork()在await關鍵字之前,根據我的理解,await關鍵字將對調用線程「返回」,並且當等待任務完成時,它將繼續從await關鍵字。所以這就是爲什麼等待MethodA();等待MethodB()可以按預期順序輸出(MethodA1,MethodA2 ... MethodA9 MethodB1 ..) –