简体   繁体   English

当您等待失败的任务时会发生什么

[英]What happens when you await a failed task

I have a theoretical question to you.我有一个理论问题要问你。 What happens if I await the Result of a Task inside another task?如果我在另一个任务中等待任务的结果会怎样? I want to know if my current system will work afterwards.我想知道我当前的系统以后是否可以工作。

A task gets launched and does some stuff.一个任务被启动并做一些事情。 At some point that task might need another one in order to process data which the current task is not able to process by itself.在某些时候,该任务可能需要另一个任务来处理当前任务无法自行处理的数据。 So I use await to ensure that the current task won't continue as long as he has not the result of the helper task.所以我使用 await 来确保当前任务不会继续,只要他没有帮助任务的结果。 But what happens if the helper fails?但是如果 helper 失败了会发生什么? Will the current task remain locked?当前任务会保持锁定状态吗?

Can I avoid this deadlock somehow (without changing the system itself - task inside task)?我能以某种方式避免这种死锁吗(不改变系统本身 - 任务内的任务)?

A task gets launched and does some stuff.一个任务被启动并做一些事情。 At some point that task might need another one in order to process data which the current task is not able to process by itself.在某些时候,该任务可能需要另一个任务来处理当前任务无法自行处理的数据。 So I use await to ensure that the current task won't continue as long as he has not the result of the helper task.所以我使用 await 来确保当前任务不会继续,只要他没有帮助任务的结果。 But what happens if the helper fails?但是如果 helper 失败了会发生什么? Will the current task remain locked?当前任务会保持锁定状态吗?

The core idea behind async and await is that asynchronous code works just about the same as synchronous code. asyncawait背后的核心思想是异步代码的工作方式与同步代码几乎相同。

So, if you have synchronous code like this:所以,如果你有这样的同步代码:

void HelperMethod()
{
  throw new InvalidOperationException("test");
}

void DoStuff()
{
  HelperMethod();
}

then you would expect DoStuff to propagate the InvalidOperationException from the helper method.那么您会期望DoStuff从辅助方法传播InvalidOperationException Similarly, that's what happens with asynchronous code:同样,这就是异步代码会发生的情况:

async Task HelperMethodAsync()
{
    throw new InvalidOperationException("test");
}

async Task DoStuffAsync()
{
    await HelperMethodAsync();
}

That is, DoStuffAsync will also propagate the InvalidOperationException .也就是说, DoStuffAsync也将传播InvalidOperationException

Now, it doesn't work exactly the same way, of course, since it must be asynchronous, but the general idea is that all your control flow such as try / catch , for loops, etc, all "just work" for asynchronous code very similarly to synchronous code.现在,它的工作方式当然不完全相同,因为它必须是异步的,但一般的想法是,所有的控制流,例如try / catchfor循环等,都“仅适用于”异步代码非常类似于同步代码。

What's actually going on is that when HelperMethod ends with an InvalidOperationException , the exception is caught and placed on the returned Task , and the task is completed.实际发生的是,当HelperMethodInvalidOperationException结束时,异常被捕获并放置在返回的Task ,任务完成。 When the await in DoStuffAsync sees that the task has completed, it examines its exceptions and re-raises the first one (in this case, there is only one, the InvalidOperationException ).DoStuffAsyncawait看到任务已完成时,它会检查其异常并重新引发第一个异常(在这种情况下,只有一个InvalidOperationException )。 And it re-raises it in a way that preserves the call stack on the exception.它以一种保留异常调用堆栈的方式重新引发它。 This in turn causes the Task returned from DoStuffAsync to be completed with that same exception.这反过来会导致从DoStuffAsync返回的Task以相同的异常完成。

So, under the covers async and await are doing a bit of work to ensure that you can just call other methods with await and use try / catch the same way as you would in synchronous code.因此,在asyncawait做了一些工作,以确保您可以使用await调用其他方法,并像在同步代码中一样使用try / catch But most of the time you don't have to be aware of that.但大多数时候你不必意识到这一点。

It's really easy to test.这真的很容易测试。 For example:例如:

[TestMethod, ExpectedException(typeof(Exception))]
public async Task DoFaultedTaskThrowOnAwait()
{
  var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
  await task;
}

[TestMethod, ExpectedException(typeof(AggregateException))]
public void DoFaultedTaskThrowOnWait()
{
  var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
  task.Wait();
}

Both tests pass, notice that Wait throws an AggregateException , and await throws the Exception .两个测试都通过了,请注意Wait会抛出一个AggregateException ,而await会抛出Exception

What happens if I await the Result of a Task inside another task?如果我在另一个任务中等待任务的结果会怎样?

You can only await a Task.Result if it is an awaitable (meaning it has a GetAwaiter method).你只能await一个Task.Result ,如果它是一个awaitable (这意味着它有一个GetAwaiter方法)。 This is rarely the case.这种情况很少见。 I assume you mean await on an inner Task.我假设您的意思是await内部任务。

But what happens if the helper fails?但是如果 helper 失败了会发生什么? Will the current task remain locked?当前任务会保持锁定状态吗?

First, im not sure what you mean by "locked".首先,我不确定您所说的“锁定”是什么意思。 The task isn't locked while you await on the inner task.当您await内部任务时,该任务不会被锁定。 Control is yielded back to the calling method until that inner task completes.控制权返回给调用方法,直到该内部任务完成。 If that inner task fails and you fail to properly handle that exception, you parent task will fault as well.如果该内部任务失败并且您未能正确处理该异常,则您的父任务也会出错。 You need to make sure you handle exceptions gracefully:您需要确保优雅地处理异常:

var task = 
  Task.Run(async () =>
           {
                 try
                 {
                        await AsyncHelper.DoSomethingAsync();
                 }
                 catch (Exception e)
                 {
                       // Handle exception gracefully
                 }
           });

If you await on the parent Task, you'll notice the inner exception propogating from the unhandled inner Task.如果您await父任务,您会注意到从未处理的内部任务传播的内部异常。

Can I avoid this deadlock somehow?我可以以某种方式避免这种僵局吗?

I see no reason why your code should deadlock in this specific case.在这种特定情况下,我认为您的代码没有理由死锁。 Not sure why this worries you.不知道为什么这让你担心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM