2012-04-06 29 views
6

我正在嘗試異步CTP,允許使用異步方法而無需編寫開始/結束方法的版本4.5。當方法返回void時,是否與任務相同?

我的第一個探針是執行一個返回void的異步方法。我看幾個例子,並做到以下幾點:

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async void method01Async() 
{ 
    await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

在我的WPF項目我有一個文本框,在那裏看到,執行異步方法的結果和一個按鈕。

在異步方法中,我使用await,即需要因爲方法是異步的,而TasEx.Run創建一個新線程來執行代碼。

我的疑問是在這一點上。在我看到的有關如何創建返回void的異步方法的幾個示例中,使用這種方式即Task.Run或TaskEx.Run。

如果我沒有錯,Task.Run創建一個新的線程在哪裏執行該方法。那麼爲什麼要使用異步方法,如果使用Task,創建一個新線程,我得到我想要的,而不是阻止主線程?

此外,如果異步方法訪問某個共享變量,我必須小心併發性,對嗎?所以我不知道使用異步方法的好處,至少在這種情況下。

事實上,我使用相同的代碼,沒有異步,沒有等待,結果是一樣的,主程序沒有阻塞,所有的工作如我所料。方法是這樣的:

private void method01Async() 
{ 
    TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("Exit method01Async: " + System.DateTime.Now); 
    }); 
} 

我的問題是,這是在方法返回無效時使用異步的正確方法?

+0

順便說一句,在Visual Studio 11 Beta版現已脫離,其中包含從異步CTP功能,有一些改進和bug修復。 – svick 2012-04-06 14:15:56

+0

在這種情況下,如果在我使用任務的方法中,並且任務使用niew線程,那麼哪種方法可以使用異步方法?如果我直接使用任務,而不使用異步方法,那麼我會得到這種行爲。 也許異步方法更有意義,如果方法返回一個值,而對於void方法,最好直接使用一個任務? – 2012-04-06 14:32:42

+0

我強烈建議所有'async'方法都返回'Task'或'Task ',除非它們是事件處理程序並且*有*返回'void'。這使您可以在必要時編寫它們,並且錯誤處理也更清晰(如果'async void'方法在WPF上下文中引發異常,則它將直接發送到UI消息循環,因此'async void'方法*而不是真正的「火和忘記」)。 – 2012-04-06 14:55:50

回答

3

如果我沒看錯的,Task.Run創建一個新的線程在哪裏執行方法。

不是究竟。 Task.Run()將在與UI線程不同的線程上運行代碼(至少使用默認的TaskScheduler)。但實際上在大多數情況下不會創建新線程,它將重用ThreadPool中的現有線程。

那麼爲什麼要用異步方法,如果用Task創建一個新的線程,我得到我想要的,而不是阻塞主線程?

async的點,在UI應用程序的上下文中,是要能夠後容易地執行在UI線程上一些代碼和異步操作完成。

所以,如果你做了method01Async「awaitable」,也就是由它返回一個Task

private async Task method01Async() 
{ 
    await Task.Run(/* whatever */); 
} 

然後,您可以從btnAsync01_Click方法等待它,如果您做了吧`異步:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after method01Async: " + System.DateTime.Now); 
} 

這樣,該方法的最後一行只有在method01Async中的Task完成執行後纔會執行。它將在UI線程上執行。

在.NET 4.0中,你使用ContinueWith()Dispatcher.Invoke()可以達到類似的效果:

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async().ContinueWith(() => 
     Dispatcher.Invoke(
      new Action(() => 
       UpdateTxtLog("after method01Async: " + System.DateTime.Now))); 
} 

我相信你會同意這是非常混亂且不易閱讀。

另外,如果異步方法訪問某個共享變量,我必須小心併發性,對嗎?

是的,你是對的。

事實上,我使用相同的代碼,沒有異步,沒有等待,結果是一樣的,主程序沒有阻塞,所有的工作如我所料。

結果肯定不是我認爲你的代碼應該做的。最後一行btnAsync01_Click將在「method01Async之後」執行,但它不會等到該方法開始時的Task完成。


作爲一個方面說明,也沒有必要在你的method01Async使用async。返回Task直接(或沒有,如果你想保持它void -returning),將工作一樣:

private Task method01Async() 
{ 
    return Task.Run(/* whatever */); 
} 
+0

那麼,當我說Task創建一個新線程時,真的是在簡化,我知道Task使用線程池中的一個現有線程,並且如果有任何人有空,那麼該任務必須等待。 在m情況下,我不想等待主方法(在這種情況下是單擊事件),因爲輔助方法(method01Async)只會生成一些不會通知主方法的東西。例如,發送電子郵件給某事。我只想發送,但如果該操作已經完成,則不需要在主應用程序中進行處理或通知。 – 2012-04-06 14:47:49

+0

是我在想什麼,在void方法中,可能不需要異步,並且對於返回值的方法,建議使用異步方法。 – 2012-04-06 14:50:36

+0

如果操作完成後你不想做任何事情,那麼是的,沒有理由使用'await'。儘管在電子郵件發送代碼中使用它可能更有意義,但更有效地使用線程池。 – svick 2012-04-06 14:52:47

1

在這兩種情況下,您都沒有真正使用異步,因爲您並未等待原始調用。這裏是你應該怎麼做:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async Task method01Async() 
{ 
    return await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

一旦將其更改爲這個(是在你的按鈕單擊事件await method01Async()的重要組成部分,它會跳回到那裏它退出後,你的「ethod01Async後:」文本日誌應該顯示十週秒鐘的延遲,就像你的「退出method01Async」日誌中method01Async方法。

+0

這不會編譯。如果你想在其中使用'await',你需要'btnAsync01_Click''async'。 – svick 2012-04-06 14:19:12

+0

是的,它需要使點擊事件異步。但是用這種方法,我也有同樣的疑問,如果在method01Async中我使用了一個任務,我正在使用一個新線程,所以我沒有看到使用異步的好處。 這讓我覺得如果異步方法在方法返回結果和void方法時更有意義,那麼最好使用直接使用任務的線程。 – 2012-04-06 14:29:07

+0

@svick是對的。我沒有真正編譯這個,所以我正在記憶中。編譯器很快就會告訴你,你不能用一個沒有用'async'標記的方法調用'await'。編輯以反映這一點。謝謝svick。 – 2012-04-06 14:29:12