WPF 在不锁定 Task..WhenAny() 的情况下调用异步代码

WPF calling asynchronous code with out locking on Task..WhenAny()

我一直在尝试在我的视图模型中设置一个非常基本的方法来临时冻结 UI 线程,同时仍然发送调度程序消息,我已经读过这个方法只适用于简单的情况而不是线程对于任务来说安全 'waiting' 而对于任务来说已经 'waiting' 会导致错误。

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

        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 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

如有任何建议,我们将不胜感激。

谢谢, 亚历克斯

您可以通过 AsyncBridge 实现。例如

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

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

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