gpt4 book ai didi

wpf - EF 6 中的异步等待问题

转载 作者:行者123 更新时间:2023-12-04 15:49:50 27 4
gpt4 key购买 nike

我正在尝试使用 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).

最佳答案

标记为“异步”的方法在第一个“等待”发生之前仍然是同步的。因此,如果初始代码中发生的任何事情花费的时间太长(我认为 200 毫秒或更长时间是 WinRT 的准则,这似乎是合理的),那么您可能希望通过更早地插入 await 来强制代码更快地返回。

例如,在您的 LoginButton_Click 中,您可以插入第一行 'await Task.Yield ()' 这将允许调用更快地返回到 UI 线程。

现在,仅通过该更改,由于异步/等待行为,所有方法仍将在 UI 线程上运行。我仍然喜欢先进行更改,因为在许多情况下这是用户实际期望发生的事情('async' 修饰符在这方面有点困惑),并且您可以在处理程序开始时执行此操作而不必弄乱堆栈更靠下的东西。

如果上述方法还不够(比如上下文初始化花费的时间太长,仍然发生在 UI 线程上,并且仍然卡住 UI,只是在一个稍微不同的时间点),我们可以做的下一步是采取这些部分不需要在 UI 线程上发生,并让 await 知道它们可以在任何线程上处理,而不仅仅是 UI 线程。对于响应能力而言,这通常是一种很好的做法,即使在代码当前运行“足够快”而不会成为明显问题的情况下也是如此。

为此,我们使用 add ConfigureAwait (false) 到任务。

  • GetUserAsync 方法应在 FirstOrDefaultAsync 调用之后添加(“链接”它)
    • 或者,恕我直言,更简洁的方法是去掉 GetUserAsync 方法中的 async/await 关键字,只返回从 FirstOrDefaultAsync 返回的任务。恕我直言,async/await 在这种方法中并没有真正“购买”任何东西,恕我直言 :)
  • 在 Authenticate 中,您应该在 GetUserAsync 调用之后添加它
    • 这里我不确定的一个潜在“陷阱”是 CurrentUser 是否已数据绑定(bind)到 UI 中。因为它是 _service 的成员,所以我猜它不是,但即使它是,我认为 WPF 可以在非 UI 线程上更新数据绑定(bind)项并且它处理将更改编码回 UI(调度程序?)线程。这与像 Silverlight 这样的框架不同,在 Silverlight 中更新数据绑定(bind)到 UI 的非 UI 上的属性将导致与手动更新目标控件相同的跨线程失败。如果我对此有误,并且 1) CurrentUser 数据绑定(bind)到您的 UI 2) 非 UI 线程上的数据绑定(bind)更新导致运行时异常,则避免 ConfigureAwait (false) 在此方法中添加。很抱歉这么长,只是想把我对这个特定修改的一些不确定联系起来。 :)
  • 在 LoginButton_Click 中,我们应该添加它,因为方法的其余部分 (this.Close) 需要在 UI 线程上发生,而此处的 ConfigureAwait(false) 会破坏它

一旦这两个更改都生效,您将 1) 尽快将控制权返回给调用者(对事件处理程序执行最少数量的同步代码)和 2) 做一些不需要的工作不需要在其他线程上的 UI 线程上,这应该意味着您的 UI 不再“卡住”。

如果在这些更改后它仍然卡住,您可能只需要在调试器下运行它,当它卡住时,中断以查看 UI 线程的堆栈是什么以找到有问题的代码。 :)

祝你好运!

关于wpf - EF 6 中的异步等待问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13249086/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com