WPF调用异步代码而没有挂起Task..WhenAny()

时间:2015-06-11 10:18:06

标签: c# .net wpf async-await .net-4.5

我一直在尝试在我的视图模型中设置一个非常基本的方法来暂时冻结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 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

任何建议都将不胜感激。

谢谢, 亚历克斯。

1 个答案:

答案 0 :(得分:1)

您可以使用AsyncBridge实现这一目标。 E.g。

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

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

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