我正在尝试使用 Entity Framework 6(代码优先)+ WPF 进行异步等待编程,但我不明白为什么在我使代码异步后 UI 仍然卡住。
这是我从第一行开始做的事情:
首先有一个响应点击按钮的事件处理程序:
private async void LoginButton_Click(object sender, RoutedEventArgs e) {
if (await this._service.Authenticate(username.Text, password.Password) != null)
this.Close();
}
然后我的服务层中有 Authenticate 方法:
public async Task<User> Authenticate(string username, string password) {
CurrentUser = await this._context.GetUserAsync(username.ToLower().Trim(), password.EncryptPassword());
return CurrentUser;
}
最后是上下文中的 EF 代码:
public async Task<User> GetUserAsync(string username, string password) {
return await this.People.AsNoTracking().OfType<User>().FirstOrDefaultAsync(u => u.Username == username && u.Password == password);
}
更新: 经过一番追踪,UI 卡住的原因原来是初始化过程。 UI 线程阻塞,直到 EF 上下文被初始化,一旦完成,实际的查询/保存过程将异步执行。
在单击处理程序开始时调用 Task.Yield() 后更新 调试输出:
53:36:378 Calling Task.Yield
53:36:399 Called Task.Yield
53:36:400 awaiting for AuthenticateAsync
53:36:403 awaiting for GetUserAsync
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.Transactions\v4.0_4.0.0.0__b77a5c561934e089\System.Transactions.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data.OracleClient\v4.0_4.0.0.0__b77a5c561934e089\System.Data.OracleClient.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'D:\SkyDrive\Works\MyApp\MyApp.UI.WPF.Shell\bin\Debug\EntityFramework.SqlServer.dll'
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.EnterpriseServices\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.EnterpriseServices.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.EnterpriseServices\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.EnterpriseServices.Wrapper.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'EntityFrameworkDynamicProxies-MyApp.Model.Domain.People'
'MyApp.vshost.exe' (Managed (v4.0.30319)): Loaded 'EntityFrameworkDynamicProxies-MyApp.Model.Domain.Security'
53:39:965 Out of GetUserAsync
53:39:968 out of AuthenticateAsync
The thread '<No Name>' (0x1e98) has exited with code 0 (0x0).
The thread '<No Name>' (0x17d4) has exited with code 0 (0x0).
The thread '<No Name>' (0x175c) has exited with code 0 (0x0).
The thread '<No Name>' (0x220) has exited with code 0 (0x0).
The thread '<No Name>' (0x1dc8) has exited with code 0 (0x0).
The thread '<No Name>' (0x1af8) has exited with code 0 (0x0).
最佳答案
标记为“async”的方法在第一个“await”发生之前仍然是同步的。因此,如果在初始代码中发生的任何事情花费的时间太长(我认为 200 毫秒或更长时间是 WinRT 的准则,这似乎是合理的),那么您可能希望通过提前插入等待来强制代码更快地返回。
例如,在您的 LoginButton_Click 中,您可以插入第一行“await Task.Yield ()”,这将允许调用更快地返回到 UI 线程。
现在,仅凭这一更改,由于 async/await 行为,这些方法仍将在 UI 线程上运行。我仍然喜欢先进行更改,因为在许多情况下,这是用户实际期望发生的事情(“async”修饰符在这方面有点令人困惑),而且您可以在处理程序开始时执行此操作而不必弄乱与堆栈更进一步的东西。
如果上述内容不充分(比如上下文初始化花费的时间太长,仍然发生在 UI 线程上,并且仍然卡住 UI,只是在一个稍微不同的时间点),我们可以做的下一步是采取那些没有的部分不需要在 UI 线程上发生,并让 await 知道它们可以在任何线程上处理,而不仅仅是 UI 线程。无论如何,这对于响应性来说通常是一个很好的做法,即使在代码当前运行“足够快”而不会引起明显问题的情况下也是如此。
为此,我们使用 add ConfigureAwait (false) 到任务。
一旦这两个变化都发生了,你们都将 1) 尽快将控制权返回给调用者(与事件处理程序同步执行最少的代码)和 2) 做不需要的工作在其他线程的 UI 线程上,这应该意味着您的 UI 不再“卡住”。
如果在这些更改后它仍然卡住,您可能只需要在调试器下运行它,当它卡住时,中断以查看 UI 线程的堆栈是什么以找到有问题的代码。 :)
祝你好运!
关于wpf - EF 6 中的异步等待问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13249086/