注册
登录
嵌入式C/C++
C#/.NET 4.5 - 当在 WPF 应用程序的 UI 线程中提供 Task.Delay 时,为什么“await Task.WhenAny”永远不会返回?
返回
C#/.NET 4.5 - 当在 WPF 应用程序的 UI 线程中提供 Task.Delay 时,为什么“await Task.WhenAny”永远不会返回?
作者:
糖果
发布时间:
2025-02-07 01:15:08 (19小时前)
给定以下代码,为什么ask.WhenAny在提供Task.Delay1 秒时永远不会返回?从技术上讲,我不确定它是否会在很长一段时间后返回,但在我手动终止进程后 15 秒左右后不会返回。根据文档,我不需要手动启动delayTask,事实上,如果我尝试手动启动,我会收到异常。 当用户在 WPF 应用程序中选择上下文菜单项时,将从 UI 线程调用该代码,尽管如果我为上下文菜单项指定的 click 方法在新线程中运行此代码,则它可以正常工作。 public void ContextMenuItem_Click(object sender, RoutedEventArgs e) { ... SomeMethod(); ... } public void SomeMethod() { ... SomeOtherMethod(); .... } public void SomeOtherMethod() { ... TcpClient client = Connect().Result; ... } //In case you're wondering about the override below, these methods are in //different classes i've just simplified things here a bit so I'm not posting //pages worth of code. public override async Task
Connect() { ... Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port); Task delayTask = Task.Delay(1000); if (await Task.WhenAny(connectTask, delayTask) == connectTask) { Console.Write("Connected\n"); ... return tcpClient; } Console.Write("Timed out\n"); ... return null; } 如果我将 ContextMenuItem_Click 更改为以下内容,则它可以正常工作 public void ContextMenuItem_Click(object sender, RoutedEventArgs e) { ... new Thread(() => SomeMethod()).Start(); ... }
收藏
举报
2 条回复
1#
回复此人
糖果
|
2021-09-23 11-56
我预测在您的调用堆栈的更深处,您正在调用Task.Wait或Task
.Result。这将导致我在我的博客上完整解释的僵局。 简而言之,这await将(默认情况下)捕获当前的“上下文”并使用它来恢复其async方法。在此示例中,“上下文”是 WPF UI 上下文。 因此,当您的代码执行await由 返回的任务时WhenAll,它会捕获 WPF UI 上下文。稍后,当该任务完成时,它将尝试在 UI 线程上恢复。但是,如果 UI 线程被阻塞(即在调用Wait或 时Result),则该async方法无法继续运行并且永远不会完成它返回的任务。 正确的解决方案是使用await代替Wait或Result。这意味着您的调用代码需要是async,并且它将通过您的代码库传播。最终,您需要决定如何使您的 UI 异步,这本身就是一门艺术。至少首先,您需要一个async void事件处理程序或某种异步 MVVM 命令(我在 MSDN 文章中探讨了异步 MVVM 命令)。从那里你需要设计一个合适的异步 UI;即,当异步操作正在进行时,您的 UI 的外观以及它允许的操作。
编辑
登录
后才能参与评论