问题描述
我的 WPF 应用程序中有以下代码,用于调用和等待来自 UI 线程上的非异步函数的 asnyc 函数而不阻塞它。这是通过使用 dispatcherFrame 在后台抽取消息来完成的:
public static void WaitWithPumping(this Task task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new dispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
dispatcher.PushFrame(nestedFrame);
task.Wait();
}
public static T ResultWithPumping<T>(this Task<T> task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new dispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
dispatcher.PushFrame(nestedFrame);
return task.Result;
}
我想在我的 Xamarin Forms iOS/Android 应用程序中使用类似的东西,以便能够使用相同的代码库。
这都是需要的,因为一个不支持 async/await 的 3rdparty 库。出于同样的原因,我不能只使用 Task.Run() 或 ConfigureAwait(false),因为这些功能需要在 UI 平台上运行,而且我需要等待结果,然后才能从相同的阻塞功能启动下一个任务。
public async void RunTaskOnUi1Async()
{
await ProcessAsync();
UpdateUiInner();
}
public async object RunTaskOnUi2Async()
{
var result = await ProcessAsync();
UpdateUiInner(result);
return result;
}
// Called from the UI thread
public void RunTasks()
{
UpdateUi();
RunTaskOnUi1Async().WaitWithPumping();
UpdateUi();
var result = RunTaskOnUi2Async().ResultWithPumping();
UpdateUi(result);
}
编辑 #1:更新示例。我知道这个例子有点蹩脚。为什么我不将 RunTasks 更改为异步?或者只是在后台线程上运行 ProcessAsync() ?好吧,我正在处理一个遗留项目(当然),RunTasks 是一个 LUA 脚本处理引擎,它调用我的 C# 代码并直接操作 UI,因此它必须在 UI 线程上运行,并且它在设计上是非异步的。改变它将是一项巨大的工作。但是我的其余代码是异步的,我能够通过使用消息泵方法在 WPF 中轻松解决这个问题。
解决方法
试试下面的代码:
public static Task<T> BeginInvokeOnMainThreadAsync<T>(Func<T> a)
{
var tcs = new TaskCompletionSource<T>();
Device.BeginInvokeOnMainThread(() =>
{
try
{
var result = a();
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
}