不使用await调用异步方法 #2

48

我有一个异步方法:

public async Task<bool> ValidateRequestAsync(string userName, string password)
{
    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(url);
        string stringResponse = await response.Content.ReadAsStringAsync();

        return bool.Parse(stringResponse);
    }
}

我像这样调用这个方法:

bool isValid = await ValidateRequestAsync("user1", "pass1");

在同步方法中,我可以调用相同的方法,而不使用await关键字吗?

例如:

public bool ValidateRequest(string userName, string password)
{
    return ValidateRequestAsync(userName, password).Result;
}

我认为这会导致死锁。

编辑

像上面那样调用该方法会使调用永远不会结束。(该方法不再到达结尾)


1
我认为在没有使用await的情况下调用异步方法只会导致该方法以同步方式被调用,其结果行为将取决于您的方法实际执行的操作。 - Chris
2
我已经尝试过了!看起来方法调用永远不会结束。 - Catalin
啊,我明白了,我实际上还没有使用过异步;我需要去读更多关于机制的内容。谢谢。 - Chris
1
你的方法可能会发生死锁,但这是由于你的逻辑问题而不是因为你以同步方式调用它的结果。 - Stilgar
@Stilgar 如果你在单线程执行上下文中运行此代码,比如UI线程,它肯定会死锁,因为线程正在等待线程变得可用。如果你在线程池线程上运行它,那就没问题了。 - Kris Vandermotten
显示剩余3条评论
3个回答

71

如果您从单线程执行上下文(例如UI线程)调用异步方法并同步等待结果,则死锁的可能性很高。在您的示例中,这种可能性为100%。

想一想,当您调用时会发生什么:

ValidateRequestAsync(userName, password).Result

你调用了ValidateRequestAsync方法,在其中你调用了ReadAsStringAsync方法。结果是一个任务将返回到UI线程,并且安排了一个继续在UI线程上执行的延续,当它变得可用时。但是,当它等待(阻塞)任务完成时,它永远不会变得可用。但任务不能完成,因为它正在等待UI线程变得可用。死锁。

有方法可以防止此死锁,但它们都是糟糕的想法。只是为了完整起见,以下可能有效:

Task.Run(async () => await ValidateRequestAsync(userName, password)).Result;

这是个不好的主意,因为你仍然会阻塞UI线程,等待并且没有做任何有用的事情。

那么解决方案是什么呢?完全异步化。原始调用者在UI线程上可能是一些事件处理程序,所以确保它是异步的。


明白了。解决方案是创建一个同步方法和一个异步方法吗? - Catalin
1
一些 asp.net web api 的操作过滤器不支持异步方法。 - Catalin
1
@RaraituL 这是真的,.ConfigureAwait 可能会对你有所帮助。但要小心:操作过滤器应该运行得很快。使用 .Result 会阻塞线程,这是应该避免的,即使在操作过滤器中也是如此。 - Kris Vandermotten
2
不,task.ConfigureAwait(false).Result 绝对行不通。它甚至无法编译,因为 ConfigureAwait() 不会返回一个 Task - svick
当您等待异步函数(Function 1)时,您正在等待的函数(Function 2)将立即返回(通常返回一个任务),从而允许其他代码继续执行。一旦等待的函数(Function 1)完成,代码将在原始等待函数(Function 2)内恢复。如果您熟悉Unity,则基本上是协同程序。 - Clonkex
显示剩余6条评论

7
你可以使用return ValidateRequestAsync(userName, password).GetAwaiter().GetResult();来验证请求。

1
哦,这真的很好看,值得更多的点赞。 - Martin Braun
4
似乎这个方法不起作用,进程停滞不前,就像您对ValidateRequestAsync(userName,password).Result所做的操作一样。Kris回应中的“坏主意”做了手脚:Task.Run(async () => await ValidateRequestAsync(userName, password)).Result。 - johey

0
对我来说,在异步调用中使用 .ConfigureAwait(false) 设置是有效的。我有类似以下的代码:
return await Entities.SingleOrDefaultAsync(.....).ConfigureAwait(false);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接