任务中的类似代码返回不同的状态代码

meJ*_*rew 4 .net c# asynchronous task

OperationCanceledException从三个不同的任务中抛出一个,每个任务都有细微的差别,如下代码所示:

static async Task ThrowCancellationException()
{
    throw new OperationCanceledException();
}

static void Main(string[] args)
{
    var t1 = new Task(() => throw new OperationCanceledException());
    t1.Start();
    try { t1.Wait(); } catch { }

    Task t2 = new Task(async () => throw new OperationCanceledException());
    t2.Start();
    try { t2.Wait(); } catch { }

    Task t3 = ThrowCancellationException();

    Console.WriteLine(t1.Status); // prints Faulted
    Console.WriteLine(t2.Status); // prints RanToCompletion
    Console.WriteLine(t3.Status); // prints Canceled
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

为什么每个任务的状态不同?

async我可以理解,标记为 的代码/lambda和未标记为 的 lambda之间存在差异,但即使在lambda 和运行相同代码的方法async之间,状态也是不同的。asyncasync

can*_*on7 6

我可以理解,标记为异步的代码/lambda 和未标记为异步的 lambda 之间存在差异,但即使在异步 lambda 和运行相同代码的异步方法之间,状态也是不同的。

这不完全正确。

如果您仔细查看该new Task(async () => throw new OperationCanceledException()),您会发现它正在调用重载new Task(Action action)(没有需要 的重载Func<Task>)。这意味着它相当于传递一个async void方法,而不是一个async Task方法。


所以:

Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }
Run Code Online (Sandbox Code Playgroud)

这编译成类似:

private static async void CompilerGeneratedMethod()
{
    throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }
Run Code Online (Sandbox Code Playgroud)

它从线程池中获取一个线程,并CompilerGeneratedMethod在其上运行。当从方法内部抛出异常时async void,该异常会在适当的地方重新抛出(在本例中,它是在 ThreadPool 上重新抛出),但方法CompilerGeneratedMethod本身会立即返回。这会导致Task t2立即完成,这就是其状态为 的原因RanToCompletion

那么异常是怎么回事呢?它即将导致您的申请失败!Console.ReadLine在 的末尾粘贴 a Main,并在您有机会按 Enter 之前看到应用程序退出。


这:

Task t3 = ThrowCancellationException();
Run Code Online (Sandbox Code Playgroud)

是非常不同的。它不会尝试在线程池上运行任何内容。ThrowCancellationException同步运行,并同步返回Task包含OperationCanceledException. Task包含 an 的 A被OperationCanceledException视为Canceled


如果要async在线程池上运行方法,请使用Task.Run. 这有一个重载,需要 a Func<Task>,这意味着:

Task t2 = Task.Run(async () => throw new OperationCanceledException());
Run Code Online (Sandbox Code Playgroud)

编译为类似:

private static async Task CompilerGeneratedMethod()
{
    throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);
Run Code Online (Sandbox Code Playgroud)

在这里,当CompilerGeneratedMethod在 ThreadPool 上执行时,它返回一个Task包含OperationCanceledException. 然后任务机制将 转变Task t2Canceled状态。


顺便说一句,如果您想在 ThreadPool 上显式运行某个方法,请避免new Task使用 ,而更喜欢使用 using 。Task.RunTPL中有很多方法是在async/await之前引入的,并且在与它一起使用时会令人困惑。

  • 好吧,在异步 lambda 和异步方法之间,返回类型会有所不同。不知道。另外*在 Main 的末尾粘贴一个 Console.ReadLine,并在您有机会按 Enter 之前查看应用程序退出。* - 事实上,我在末尾使用断点检查它,但没有注意到异常。很好的答案! (2认同)