2016-11-23 67 views
2

閱讀Task-based Asynchronous Pattern由斯蒂芬Toub我想看看取消如何工作的任務。 在部分消費基於任務的異步模式等待,在3-RD段落它說:等待任務以取消狀態結束不會丟

如果任務或任務TResult>在取消狀態等待結束,一個 OperationCanceledException將被拋出。

我想在下面的代碼中看到這一點。

static void Main(string[] args) 
{ 
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
    CancellationToken cancellationToken = cancellationTokenSource.Token; 

    Task<int> valueTask = DoStuffAsync(cancellationToken); 
    Thread.Sleep(TimeSpan.FromSeconds(1)); 
    cancellationTokenSource.Cancel(); 

    Console.WriteLine("value task's status: {0}", valueTask.Status); 
    Console.ReadLine(); 
} 

而且DoStuffAsync()方法

static async Task<int> DoStuffAsync(CancellationToken cancellationToken) 
{ 
    await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); 
    return 42; 
} 

執行此代碼不拋出任何異常,它只是打印:

值任務狀態:已取消

現在,我的期望是在DoStuffAsync()方法s ince await Task.Delay(...)被取消,我們已經等待任務以cancelled狀態結束,因此應該拋出異常(根據TAP文檔中的引用),但是如果我在Console.ReadLine()上放置斷點並檢查valueTask它的狀態爲已取消,異常爲null

任何人都可以幫助我理解,如果我誤讀了文檔,或者我提出的代碼是不正確地複製案件?

+2

引用內容涉及您在等待取消任務時發生的情況。你*永遠不會等待任務*!沒有人說'Task.Exception'會有一個異常,這只是愚蠢的:)你必須明白,你有兩個獨立的執行線程在這裏,你永遠不會同步它們 - 你到底會在什麼地方發生異常?在線程池中,查殺整個應用程序? – Luaan

回答

3

該方法返回任務本身,結果本身永遠不會被訪問。如果您嘗試訪問valueTask.Result,則會得到TaskCanceledException(在AggregateException中)。

同樣,如果你想要await valueTask(Main將不得不通過異步),你會嘗試獲得結果,在這種情況下,異常也會被拋出。這是上述段落中描述的行爲。

線索在於Task對象是有效的,但結果並不是因爲如果任務被取消,從未執行await之後的代碼。例如與:

static async Task<int> DoStuffAsync(CancellationToken cancellationToken) 
{ 
    var delay = Task.Delay(5000, cancellationToken); 
    Console.WriteLine("before await"); 
    await delay; 
    Console.WriteLine("after await"); 
    return 42; 
} 

如果任務被取消,第二個writeline永遠不會執行。 只要沒有結果被DoStuffAsync返回的任務訪問,該任務就是一個有效的對象,剛剛取消。訪問結果將強制運行時確認任務未完成並拋出異常。

如果異步方法不返回任務,你也將得到一個TaskCancelled例外:

static async void Main() 
{ 
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
    CancellationToken cancellationToken = cancellationTokenSource.Token; 

    DoStuffAsync(cancellationToken); 
    Thread.Sleep(TimeSpan.FromSeconds(1)); 
    cancellationTokenSource.Cancel(); 
    Console.ReadLine(); 
} 

// Define other methods and classes here 
static async void DoStuffAsync(CancellationToken cancellationToken) 
{ 
    await Task.Delay(5000, cancellationToken); 
} 

因爲將返回它可以包含狀態沒有任務的對象,編譯器警告執行無法完全運行異步代碼的代碼並引發異常。

+0

謝謝,我正在尋找的答案! – Michael

0

編輯:這個答案是要使用Task內的TaskCanceledException的情況下,但你的問題是得到它的它之外,但它可能仍然是一些有用的。 ;-)

在你TaskDoAsyncStuff功能),你必須檢查CancellationToken,然後自己扔TaskCanceledException或只是調用它ThrowIfCancellationRequested()

2

當您嘗試從此任務獲得結果時,您將獲得OperationCanceledException

試着在你的Main方法的末尾添加一行:

valueTask.GetAwaiter().GetResult(); 

,你會得到TaskCanceledException是desendent從OperationCanceledException,取消任務await也將拋出TaskCanceledException

您還可以使用valueTask.ResultvalueTask.Wait()但他們會拋出AggregateException與單個成員TaskCanceledException

爲了更好地理解await內部發生的事情,看看它是如何轉換到狀態機tryroslyn可能是有用的。

+0

感謝您的回答,最初它不明確,但現在閱讀@ Me.Name的答案後,它是有道理的,並感謝鏈接! – Michael