Async-await:线程会一直运行到我的等待吗?

我一直认为如果我调用一个异步函数,线程就会开始执行这个异步函数,直到它看到一个等待。我认为它会向上调用堆栈查看调用者是否没有等待,而不是无所事事地等待。如果没有,则执行代码。


考虑以下(简化的)代码:


async Task<string> FetchCustomerNameAsync(int customerId)

{

    // check if customerId is positive:

    if (customerId <= 0) throw new ArgumentOutofRangeException(nameof(customerId);


    // fetch the Customer and return the name:

    Customer customer = await FetchCustomerAsync(customerId);

    return customer.Name;

}

现在,如果我的异步函数FetchCustomerNameAsync(+1)不等待就调用会发生什么:


var myTask = FetchCustmerNameAsync(+1);

DoSomethingElse();

string customerName = await myTask;

FetchCustomerNameAsync, 以 +1 的参数值调用

FetchCustomerNameAsync检测到它customerId是阳性的,所以也不例外

FetchCustomerNameAsync 电话 FetchCustomerAsync

里面的某个地方FetchCustomerAsync是一个等待。发生这种情况时,线程会向上调用调用堆栈,直到其中一个调用者没有等待。

FetchCustomerNameAsync 正在等待,所以调用堆栈

我的功能还没有等待,继续 DoSomethingElse()

我的功能满足等待。

我的想法是,在满足我函数中的 await 之前,已经完成了对参数值的检查。


因此,以下应在 await 之前导致异常:


// call with invalid parameter; do not await

var myTask = FetchCustmerNameAsync(-1);      // <-- note the minus 1!

Debug.Assert(false, "Exception expected");

我认为虽然我没有等待,但在Debug.Assert.


然而,在我的程序中,在 Debug.Assert为什么之前没有抛出异常?到底发生了什么?


翻翻过去那场雪
浏览 385回答 3
3回答

繁星淼淼

异常仅在等待任务时传播您不能在不等待任务的情况下处理异常。异常仅在线程/任务内传播。因此,如果您不等待,异常只会停止任务。如果在您等待之前抛出异常,它将在您实际等待时传播。之前做所有的验证,然后做异步工作。所以,我建议你之前验证:ValidateId(id); // This will throw synchronously.Task<Customer> customer = FetchCustomerAsync(id).ConfigureAwait(false);DoSomethingElse();return await customer.Name;这是实现您想要的并行性的最佳方式。

慕莱坞森

您是对的,该线程执行异步函数直到它看到等待。事实上,你ArgumentOutofRangeException是由你调用的线程抛出的FetchCustmerNameAsync。即使它是同一个线程也不会得到异常的原因是因为当您await在函数内部使用时,AsyncStateMachine构建了 a 。它将所有代码转换为状态机,但重要的部分是它如何处理异常。看一看:这段代码:public void M() {&nbsp; &nbsp; var t = DoWork(1);}public async Task DoWork(int amount){&nbsp; &nbsp; if(amount == 1)&nbsp; &nbsp; &nbsp; &nbsp; throw new ArgumentException();&nbsp; &nbsp; await Task.Delay(1);}转换为(我跳过了不重要的部分):private void MoveNext(){&nbsp; &nbsp; int num = <>1__state;&nbsp; &nbsp; try&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; TaskAwaiter awaiter;&nbsp; &nbsp; &nbsp; &nbsp; if (num != 0)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (amount == 1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new ArgumentException();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; awaiter = Task.Delay(1).GetAwaiter();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!awaiter.IsCompleted)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Unimportant&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Unimportant&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; catch (Exception exception)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; <>1__state = -2;&nbsp; &nbsp; &nbsp; &nbsp; <>t__builder.SetException(exception); // Add exception to the task.&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; <>1__state = -2;&nbsp; &nbsp; <>t__builder.SetResult();}如果你跟着<>t__builder.SetException(exception);( AsyncMethodBuilder.SetException),你会发现它最终会调用task.TrySetException(exception);which 将异常添加到任务的 中exceptionHolder,可以通过Task.Exception属性检索。

catspeake

一个简化的 MCVE :&nbsp; &nbsp; static async Task Main(string[] args)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; try&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // enable 1 of these calls&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var task = DoSomethingAsync();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; var task = DoSomethingTask();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("Still Ok");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await task;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; catch (Exception ex)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(ex.Message);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; private static async Task DoSomethingAsync()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; throw new NotImplementedException();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; private static Task DoSomethingTask()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; throw new NotImplementedException();&nbsp; &nbsp; &nbsp; &nbsp; return Task.CompletedTask;&nbsp; &nbsp; }当您调用 DoSomethingAsync 时,您将看到“Still Ok”消息。当您调用 DoSomethingTask 时,您将获得您期望的行为:WriteLine 之前的立即异常。
打开App,查看更多内容
随时随地看视频慕课网APP