2016-11-23 73 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