我遇到了一种情况,使我觉得自己不像我想象的那样理解异步/等待机制。
我有一个Windows桌面应用程序,该应用程序主要是WPF,但使用WinForms主机显示第三方COM对象。加载COM对象的过程非常缓慢,因此我一直在尝试将这些对象的创建和初始化移至一个任务,以在发生该工作时释放UI,但是我发现在某些情况下,等待Task.Run()返回,它不在UI线程上。结果,当任务返回后更改WinForms主机上的Visible属性时,由于跨线程调用而引发了该错误。
调用函数如下:
public async Task<bool> LoadPreview(string filePath)
{
bool result;
try
{
await _semaphore.WaitAsync();
result = await Task.Run(() => CreateAndInitializePreviewer(filePath));
if (result)
{
Visible = false; // <-- occasionally crashes because I'm not on the UI thread
_currentHandler.DoPreview();
Visible = true;
}
}
finally
{
_semaphore.Release();
}
return result;
}
CreateAndInitializePreviewer中的代码没有任何异步/等待调用。我已经验证了在调用Task.Run()之前,我始终在UI线程上。
关于我应该寻找的任何建议都会导致await Task.Run()返回到另一个线程?任何想法都表示赞赏。
答案 0 :(得分:4)
加载COM对象的过程非常缓慢,因此我一直在尝试将这些对象的创建和初始化转移到任务中,以在发生工作时释放UI
除非您的COM对象是自由线程的(这不太可能),否则这可能行不通。如果要从UI线程中删除该工作,则可能需要创建一个单独的STA线程来保存COM对象,并封送对该线程的调用/从该线程封送调用-这意味着对该COM的 all 调用对象,因为它将存在于另一个STA线程中。 It's fairly straightforward in WPF to create a second UI (STA) thread;相当困难,但在WinForms中仍然可以实现。
关于我应该寻找的任何建议都会导致等待的Task.Run()返回到另一个线程?
是的。 await
will capture SynchronizationContext.Current
(如果不是null
,并且在这种情况下不应为null
。它应该是DispatcherSynchronizationContext
(通过向WPF调度程序发送一条消息继续执行)的实例,或者是WinFormsSynchronizationContext
(通过向WinForms winproc发送一条消息继续执行)的实例。
我最初的猜测是,由于WinForms-in-WPF体系结构,SynchronizationContext.Current
发生了一些奇怪的事情。