WPF 调用异步代码而不锁定 Task.WhenAny()

本文关键字:Task WhenAny 锁定 调用 异步 代码 WPF | 更新日期: 2023-09-27 17:55:41

我一直在尝试在我的视图模型中设置一个非常基本的方法,用于暂时冻结 UI 线程,同时仍然泵送调度程序消息,我已经阅读此方法仅适用于简单情况并且线程不安全,因为"等待"任务而已经"等待"任务会导致错误。

但这是我到目前为止编写的方法:

        public T WaitForTask<T>(Task<T> task, bool throwException = false)
        {
            WaitForTask((Task)task, throwException);
            return (!task.IsCompleted || task.IsFaulted || task.Exception != null) ? default(T) : task.Result;
        }
        public void WaitForTask(Task task, bool throwException = false)
        {
            if (task == null)
                throw new ArgumentNullException("task");
            // Wait for the task to complete
            IsWaiting = true;
            using (MouseWait.Begin())
            {
                var runTask = UIDispatcher.InvokeAsync(async () =>
                    {
                        await task;
                    });
                // Wait for the taske to finish
                while (!task.IsCompleted)
                {
                    DispatcherPush();
                    Thread.Sleep(10);
                }
                var exception = task.Exception;
                if (exception != null)
                {
                    if(exception is AggregateException)
                    {
                        var aggregateException = (AggregateException)exception;
                        log.ErrorFormat("{0}.WaitForTask(): {1}", GetType().Name, ExceptionUtility.JoinAggregateExceptionMessages(aggregateException));
                    }
                    else
                    {
                        var exceptionMessage = ExceptionUtility.JoinExceptionMessages(exception);
                        log.ErrorFormat("{0}.WaitForTask(): {1}", GetType().Name, exceptionMessage);
                    }
                    if (throwException)
                        throw exception;
                }
            }
            IsWaiting = false;
        }

这适用于一次等待单个异步任务,这是我当前应用程序范围内所需要的全部,但是当我开始优化我的异步时,我发现许多地方使用 Task.WhenAny() 方法很合适。但是,调用Task.WhenAny()会导致应用程序锁定...

我认为我的问题可能与此线程中的答案有关:C#/.NET 4.5 - 为什么在 WPF 应用程序的 UI 线程中提供 Task.Delay 时"等待任务.WhenAny"永远不会返回?

任何建议将不胜感激。

谢谢亚历克斯。

WPF 调用异步代码而不锁定 Task.WhenAny()

你可以用AsyncBridge来实现这一点。 例如

using (var asyncBridge = AsyncHelper.Wait)
{
    asyncBridge.Run(DoWorkAsync());
}

它的工作原理是安装自定义SynchronizationContext并使用它来等待任务,同时释放 UI 线程。

因为它使用同步上下文而不是直接使用调度程序,所以它本质上与框架无关,并且适用于WPF和 Winforms,这就是引入SynchronizatonContext概念的原因。它基本上抽象了如何将工作分配给各种线程。