2016-07-11 60 views
2

我試圖抓住異步等待模式。繼this例如,我實現了 這個小程序:async等待錯誤的執行順序

private async void asynchWork() 
{ 
    // Line 1 
    Task<string> readTask = asynchAwaitWork(); 

    // Line 2 
    synchWork(); 

    // Line 3 
    string result = await readTask; 

    // Line 4 
    Debug.WriteLine(result); 
    Debug.WriteLine("The End"); 
} 
private void synchWork() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     Debug.WriteLine("SynchWork For i : " + i); 
     Thread.Sleep(750); 
    } 
} 

private async Task<string> asynchAwaitWork() 
{ 
    for (int i = 0; i < 15; i++) 
    { 
     Debug.WriteLine("A-SynchWork For i : " + (i+111)); 
     Thread.Sleep(750); 
    } 

    return "finished"; 
} 

我預計,1號線執行,那麼2號線和4號線將不得不等待,直到3號線完成並返回一個字符串。 但看着輸出synchWork方法在asynchWork方法後執行。

A-SynchWork對於i:111

A-SynchWork對於i:112

A-SynchWork對於i:113

A-SynchWork對於i:114

A-SynchWork For I:115

...

A-SynchWork對於i:125

SynchWork對於i:0

SynchWork對於i:1個

SynchWork對於i:2

SynchWork對於i:3

SynchWork對於i:4

已完成

終結

我預期當asynchWork方法是buisy並等待在第3行的結果被在時所執行的方法synchWork。 這個例子中我誤解了什麼?還是僅僅是Debug.WriteLine的輸出是按順序執行的,但是真正的工作順序是按照我的期望執行的?

編輯: 隨着意見和答案的幫助下,我瞭解到:

  • 不要忽視警告,並研究它們的含義;使用await關鍵字允許等待)

  • 由該方法內部創建的Task執行的操作的結果。

編輯2:

  • 但任務必須啓動!否則將不會有並行處理,編譯器將通過主線程追蹤它,而Line 3實際上只有Line 4的阻塞函數不多。

因爲Thread.Sleep(750)只是一個模擬的工作,我並不真的需要Delay這也適用:

private async Task<string> asynchAwaitWork() 
{ 
    await Task.Run(()=> { 
     for (int i = 0; i < 15; i++) 
      { 
       Debug.WriteLine("A-SynchWork For i : " + (i+111)); 
       Thread.Sleep(750); 
      } 
      }); 
    return "finished";    
} 

非常感謝你。我學到了很多:)

+3

你應該得到'asynchAwaitWork'的編譯器警告,它給出了爲什麼它不起作用的線索 –

+0

@ScottChamberlain等待丟失它說。因此它將被同步執行。我只是不太明白爲什麼從這個小小的信息。 –

+3

@MongZhu然後對這個警告做一些研究。這裏有很多信息。 – Servy

回答

2

展望:

當3號線已執行,保證您的任務你有在1號線有完成執行不管是裏面。

在第2行,您不能保證任務已開始執行或已完成。你不知道它是否會平行運行。你所知道的是有一個任務可以被安排執行。

在函數​​中創建的任務的實現是完全同步的。當實現細節返回將最終(在這種情況下是立即)的任務時返回一個字符串時,該實現細節將導致它完全完成打印內容。

請注意,您創建的任務不會打印任何內容。在當前實施中打印您的「A-SynchWork For i」消息是創建您的任務的副作用。

+0

很好的解釋!所以這意味着在'asynchAwaitWork'部分:'異步任務'導致創建任務,但它永遠不會並行運行,因爲沒有一行代碼表示「開始(...)」或「潤(...)'。這就是爲什麼它也不會打印任何東西?=!當'asynchAwaitWork'到達返回語句時,它將返回值賦給第3行傳遞給'result'的任務。 你答案的最後部分真的很有啓發性! –

+0

更多的討論可能對聊天格式更好。但是你的'asynchAwaitWork'和'console.WriteLine(「這是一個副作用」)相當。返回Task.FromResult(「完成」)。等待該任務與之前的WriteLine無關。 – Martijn

1

只需更換 Thread.Sleep(750);await Task.Delay(750);在asynchAwaitWork 的事情是,asynchAwaitWork開始在主線程工作。異常的工作 - 這是爲了延續。使用await關鍵字,您創建延續,這就是爲什麼它開始並行工作。 還要將當前線程ID添加到輸出中以查看發生了什麼。例如:

private async void asynchWork() 
{ 
    // Line 1 
    Task<string> readTask = asynchAwaitWork(); 

    // Line 2 
    synchWork(); 

    // Line 3 
    string result = await readTask; 

    // Line 4 
    Debug.WriteLine(result); 
    Debug.WriteLine("The End"); 
} 
private void synchWork() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     Debug.WriteLine("SynchWork For i : " + i + " and thareadId = " + Thread.CurrentThread.ManagedThreadId); 
     Thread.Sleep(750); 
    } 
} 

private async Task<string> asynchAwaitWork() 
{ 
    for (int i = 0; i < 15; i++) 
    { 
     Debug.WriteLine("A-SynchWork For i : " + (i + 111) + " and thareadId = " + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(750); 
     //Thread.Sleep(750); 
    } 

    return "finished"; 
} 

正如人們注意到的那樣,Task.Delay(750)將使用相同的同步上下文(如果創建的話)。 個例子,假如你是從WPF

[STAThread] 
void Main() 
{ 
    var window = new Window(); 
    window.Loaded += (s, e) => { 
     asynchWork(); 
    }; 
    window.ShowDialog(); 
} 

運行的代碼,你會看到所有發生在同一個線程。 因此,請注意,您可以使用await Task.Delay(750).ConfigureAwait(false);來避免這種情況。只有在你的主程序

+0

如果這是在控制檯應用程序中運行,它們將並行運行,但如果在設置了「SyncronisationContext.Current」的情況下運行它,它將運行第一個「asynchAwaitWork」,然後是所有的「synchWork」,然後是其餘的'asynchAwaitWork'。你需要做'等待Task.Delay(750).ConfigureAwait(false);'或者做任務 readTask = Task.Run(()=> asynchAwaitWork());' –

+0

yep,Task.Delay(750)繼續當前的情況。因此,如果我們在U​​I線程中的WPF應用程序(例如)中運行它,則將在UI線程中運行延續。謝謝@ScottChamberlain,你是對的。 – tym32167

+0

@ tym32167「使用await關鍵字創建延續」爲什麼?你能詳細說明一下嗎? –