c# - 任务异步/等待不适用于 WPF,因为它在其他场景中有效

标签 c# wpf async-await

我遇到了一个非常奇怪的行为。经过大量挖掘后,我能够找到一个场景,该场景表明(显然)直接从 WFP 应用程序使用任务等待无法按预期工作。但是,创建任务并在其中执行等待工作正常。

我使用以下步骤来说明(使用 VS 2013)。在新的 WPF 应用程序中使用此 main.xaml.cs:

using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        static async Task<bool> Test_int()
        {
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

            Thread t = new Thread(() =>
            {
                Thread.Sleep(1000);
                tcs.SetResult(true);
                //Console.WriteLine("TCS set");
            });
            t.Start();

            //int i = tcs.Task.Result;   //<-- this always works, but doesn't take    advantage of await semantics
            var ret = await tcs.Task;

            return ret;
        }

        static void Test()
        {
            var tt = Test_int();
            //(1)
            tt.Wait();
            //Console.WriteLine("Test done");
        }

        public MainWindow()
        {
            InitializeComponent();

            //option 1 -- works
            Task t = new Task(Test);
            t.Start();
            t.Wait();

            //option 2 -- hangs indefinitely
            Test();
        }
    }
}

我看到的行为是运行方法 Test() 直接导致应用程序挂起(在标记为 (1) 的等待行中),而在任务中运行它会正确运行并完成。

我通过在任务上下文中运行解决了我最初的问题,但我想了解问题的原因。顺便说一句,在控制台应用程序中运行时,相同的 Test() 方法确实可以直接工作。

为什么直接从 WPF 应用程序运行时不等待工作(以相同的方式)?

最佳答案

您遇到了 classic deadlock scenario我在我的博客上描述的。总之,await 默认情况下会捕获一个“上下文”并使用它来恢复 async 方法。在这种情况下,它是一个 UI 上下文,如果您阻塞 UI 线程(通过调用 Wait),则 async 方法无法恢复并且永远不会完成。

正确的解决方案是使用 await 而不是 Wait

此外,您不应使用Task 构造函数、StartThread(使用Task.Run 相反,但前提是您需要在后台线程上执行代码)。

我建议您阅读我的 async intro blog post然后是我的 async best practices MSDN article .他们应该可以帮助您。

关于c# - 任务异步/等待不适用于 WPF,因为它在其他场景中有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27573834/

相关文章:

c# - 将嵌套的 for 循环转换为单个 LINQ 语句

c# - 如何将列表绑定(bind)到 Data Grids 数据源的子类中的组合框?

wpf - FixedDocument中的WPF表

c# - System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start 有大量的自用时间

javascript - 访问 setTimeout promise 的值而不等待它

c# - 为什么编译器认为Environment.Exit可以返回?

c# - UITableViewDelegate 中的问题 - RowSelected 给出了错误的 NSIndexPath

wpf - 使用自动高度/宽度时,拖动 GridSplitter 会导致单元格增长

wpf - 从 MahApps FlipView 中删除边框

rust - 如何在 Rust 中使用 Windows IOCP 实现异步函数?