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"永远不会返回?
任何建议将不胜感激。
谢谢亚历克斯。
你可以用AsyncBridge来实现这一点。 例如
using (var asyncBridge = AsyncHelper.Wait)
{
asyncBridge.Run(DoWorkAsync());
}
它的工作原理是安装自定义SynchronizationContext
并使用它来等待任务,同时释放 UI 线程。
因为它使用同步上下文而不是直接使用调度程序,所以它本质上与框架无关,并且适用于WPF
和 Winforms,这就是引入SynchronizatonContext
概念的原因。它基本上抽象了如何将工作分配给各种线程。