下面是一些在LinqPad中“按原样”运行的代码。它表明了我对await
正在做什么的误解。请进一步了解结果和我的问题。
void Main()
{
var cts = new CancellationTokenSource();
StartIt(cts.Token);
Thread.Sleep(500);
"Cancelling".Dump();
cts.Cancel();
}
// Define other methods and classes here
async Task StartIt(CancellationToken token)
{
"StartIt".Dump();
var o = new TestClass();
await o.Step0(token);
}
public class TestClass
{
public async Task Step0(CancellationToken token)
{
var t = Step1(token);
("Step0.Task.Id => " + t.Id).Dump();
await t;
("Step0.IsCancelled? " + t.IsCanceled).Dump();
}
public async Task Step1(CancellationToken token)
{
var t = Step2(token);
("Step1.Task.Id => " + t.Id).Dump();
try {
await t;
}
catch (OperationCanceledException)
{
"Step1.OperationCanceledException".Dump();
}
("Step1.IsCancelled? " + t.IsCanceled).Dump();
}
public Task Step2(CancellationToken token)
{
return Task.Run(() =>{
Thread.Sleep(3000);
token.ThrowIfCancellationRequested();
"Done".Dump();
}, token);
}
}
以上产生:
StartIt
Step1.Task.Id => 17
Step0.Task.Id => 18
Cancelling
Step1.OperationCanceledException
Step1.IsCancelled? True
Step0.IsCancelled? False
我的问题:
我期待Step0和Step1的任务。我是一样的。
(因此)我希望取消会传播方法,以便我可以询问t.IsCanceled
并采取适当的行动。
但是我得到了另一个Task
返回Step0
。我错过了什么?
由于
答案 0 :(得分:3)
我建议你阅读我的introduction to async
。此外,还有一个内置于LinqPad的优秀教程(示例 - >下载/导入更多样本 - >在C#5交互式教程中使用异步)。
async
代码中的任务标识符可能有点棘手。会发生什么是每个async Task
方法创建自己的任务来表示该方法调用。因此,Task.Run
会创建Task
并将其返回到Step2
和Step1
(您的测试中为17
)。 Step1
还会创建Task
并将其返回到Step0
(您的测试中为18
)。 Step0
还会创建Task
并将其返回StartIt
,StartIt
也会创建Task
并将其返回Main
。
因此,Task
返回的Task.Run
(Task
返回的Step2
)将被取消。 Step1
捕获已取消的异常,因此会正常返回,而Task
返回的Step1
不会被取消。
如果要传播异常,请不要捕获已取消的异常(或者如果您想捕获它以进行记录,则通过throw;
重新抛出异常)。