为什么这个async/await不能异步运行?

本文关键字:不能 异步 运行 await async 为什么 | 更新日期: 2023-09-27 18:05:07

我已经搜索了这个问题的答案,但是根据许多指南和SO问题,这个代码对我来说仍然是正确的,但它是同步运行的。

private void CheckConditions()
{
    foreach (var obj in myObjects)
    {
        if (obj.ConditionMet)
        {
            HandleConditionAsync(obj);
        }
    }
    DoOtherWork();
}
private async void HandleConditionAsync(MyObject obj)
{
    // shouldn't control transfer back to CheckConditions() here while we wait for user input?
    string userInput = await obj.MessagePromptAsync("hello user");
    DoSomeBookkeeping(obj);
}
// (MyObject.cs)
private MessagePrompt messagePrompt; // inherits from UserControl
public async Task<string> MessagePromptAsync(string prompt)
{
    return await Task.FromResult<string>(messagePrompt.Prompt(prompt));
}
// (MessagePrompt.cs)
public string Prompt(string prompt)
{
    this.UIThread(() => this.SetMessagePrompt(prompt));
    userInputAutoResetEvent.WaitOne();
    return myResult; // set in a button handler that also sets the AutoResetEvent
}

我打算让CheckConditions()愉快地继续下去,但它被卡在MessagePrompt的AutoResetEvent上,尽管我的异步/等待。我能想到的唯一一件事可能是错误的是,也许MessagePrompt的方法不能异步运行,由于一些限制从UserControl,它使用的UI线程引用,或者可能在堆栈顶部的非异步方法。

为什么这个async/await不能异步运行?

您的代码中没有任何异步内容。您拥有的唯一任务是从结果值创建的,这意味着Prompt()方法必须完成并返回其结果,然后才能返回Task对象以等待。该对象将已经完成,因此它上面的任何await都将立即完成,一旦它有Task等待。

也许你的意思是:

public async Task<string> MessagePromptAsync(string prompt)
{
    return await Task.Run(() => messagePrompt.Prompt(prompt));
}

或者(如果在MessagePromptAsync()方法中确实没有其他内容):

public Task<string> MessagePromptAsync(string prompt)
{
    return Task.Run(() => messagePrompt.Prompt(prompt));
}

注意,这可能导致不同的问题,这取决于DoOtherWork()UIThread()实际做什么。如果你的UI线程在DoOtherWork()中被绑定,而UIThread()方法正在包装Dispatcher.Invoke()或类似的方法,那么你就会出现死锁。

如果这不能解决您的问题,请提供一个良好的最小化,完整和可验证的代码示例,可靠地再现问题

您还需要使CheckConditions()异步,然后等待对HandleConditionAsync(MyObject obj)的调用。CheckConditions()在示例中同步运行。

private async Task CheckConditionsAsync()
{
    foreach (var obj in myObjects)
    {
        if (obj.ConditionMet)
        {
            await HandleConditionAsync(obj);
        }
    }
    DoOtherWork();
}

另外,这只是一个最佳实践的事情,一个异步方法应该总是返回一个任务在可能的时候。我唯一一次使用async void是为了与事件处理程序兼容。你可以看到我这样修改了CheckConditions(), HandleConditionAsync(MyObject obj)也应该这样修改。我还更改了方法名称,以表示它的异步行为。

如果您需要运行一个同步返回Task的方法(您不应该这样做,这表明您的设计存在错误),您可以使用Task.FromResult(MyMethodAsync())运行它。同样,尽可能避免这样做,因为它首先违背了使方法异步化的目的。