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

时间:2016-08-31 22:14:38

标签: c# async-await

我已经找到了答案,但根据许多指南和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上,尽管我的异步/等待。我唯一能想到的可能是错误的是,由于UserControl的一些限制,它对UI线程引用的使用,或者可能是非异步方法,MessagePrompt的方法可能无法异步运行。堆栈顶部。

2 个答案:

答案 0 :(得分:4)

您的代码中没有任何异步内容。您从结果值创建的唯一任务,意味着Prompt()方法必须完成并返回其结果,然后才能让Task对象返回等待。该对象已经完成,因此一旦await等待Task,该对象就会立即完成。{/ p>

也许你的意思是:

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()或类似,那么你就会陷入僵局。

如果这不能解决您的问题,请提供可靠地重现问题的好Minimal, Complete, and Verifiable code example

答案 1 :(得分:1)

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

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

此外,这只是一种最佳实践,异步方法应尽可能始终返回Task。我唯一必须使用async void是为了与事件处理程序兼容。您可以通过这种方式看到我已更改CheckConditions(),并且应同样修改HandleConditionAsync(MyObject obj)。我还更改了方法名称以表示它的异步行为。

如果你需要运行一个同步返回Task的方法(你不应该这样做,这表明你的设计不正确),你可以用{{1}运行它}}。同样,避免在任何地方都这样做,它首先使得方法异步的目的失败。