不知道我是否对我对async await如何工作的理解感到困惑,但这是我坚持的问题。考虑一个人为的例子
public async void LoginButtonClicked()
{
//create a continuation point so every following statement will get executed as ContinueWith
await Task.FromResult(0);
//this call takes time to execute
Remote.Login("user","password");
}
public void LoginButtonClicked()
{
Task.Run(()=>{ Remote.Login("user","password");});
}
我喜欢使用方法1,因为我不想使用Task.Run来旋转长时间的工作,而是我更喜欢框架处理这个形式我。但问题是对方法1的调用似乎是阻塞的。
如果您调用的所有长时间运行的操作都是异步的,则使用await / async仅阻止您阻止UI。在您的示例中,您的Remote.Login
是同步调用,因此无论先前的await
行如何操作,都将阻止您的UI。
您需要获得实际长时间运行操作的异步版本(例如返回Task
的东西)或者如果不可能,那么您可以使用Task.Run
将此工作移至ThreadPool
。
如果可能,你想要什么:
public async void LoginButtonClicked()
{
await Remote.LoginAsync("user","password");
// do anything else required
}
每个异步方法都有其上下文。
当任务开始时,它可能会在新的SynchronizationContext
中运行。 “可能”,因为如果任务已经完成,如Task.FromResult(0)
,则不会创建其他SynchronizationContext
并使用原始的Task.ConfigureAwait(continueOnCapturedContext: false)
。
等待任务意味着当任务完成时,下一个语句将在原始SynchronizationContext中运行。
可以使用Task.FromResult(0).ConfigureAwait(false)
更改此行为。这意味着下一个语句将在相同的上下文中继续。但是,这将通过执行Remote.Login("user","password");
来改变,因为任务已经完成并且将使用原始上下文。
因此,您的public async void LoginButtonClicked()
{
await Task.Delay(5000).ConfigureAwait(false);
Remote.Login("user","password");
}
将在原始上下文中运行,从而阻止在相同上下文中运行的UI线程。
如果你有类似的东西:
Remote.Login("user","password");
然后Remote.LoginAsync()
将在线程池上下文中执行,因此处于与原始UI上下文不同的上下文中。
因此,修复代码的最佳方法是创建一个Task.ConfigureAwait(false)
,如@Nicholas W回答中所述。
关于性能的说明:如果你有一个带有多个await语句的异步方法,并且你不需要其中一些等待在UI或web应用程序线程上工作,那么你可以使用 async Task LoginAsync(string user, string password)
{
Remote.Login(user, password);
await Task.FromResult(0);
}
来防止多个切换到UI /切片执行时间的web-app上下文。
您必须创建Remote.Login的异步版本
public async void LoginButtonClicked()
{
await LoginAsync("user", "password");
}
并称之为
qazxswpoi