C#“等待”实际上会返回什么?

时间:2013-09-11 16:15:36

标签: c# async-await

下面是一些在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。我错过了什么?

由于

1 个答案:

答案 0 :(得分:3)

我建议你阅读我的introduction to async。此外,还有一个内置于LinqPad的优秀教程(示例 - >下载/导入更多样本 - >在C#5交互式教程中使用异步)。

async代码中的任务标识符可能有点棘手。会发生什么是每个async Task方法创建自己的任务来表示该方法调用。因此,Task.Run会创建Task并将其返回到Step2Step1(您的测试中为17)。 Step1还会创建Task并将其返回到Step0(您的测试中为18)。 Step0还会创建Task并将其返回StartItStartIt也会创建Task并将其返回Main

因此,Task返回的Task.RunTask返回的Step2)将被取消。 Step1捕获已取消的异常,因此会正常返回,而Task返回的Step1不会被取消。

如果要传播异常,请不要捕获已取消的异常(或者如果您想捕获它以进行记录,则通过throw;重新抛出异常)。