Reputation: 9
I'm running into a situation that's making me think I don't understand async / await mechanics as well as I thought.
I've got a Windows desktop app which is mostly WPF but uses a WinForms host to show a 3rd party COM object. The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens, but I'm finding that in some situations when the await Task.Run() returns, it's not on the UI thread. As a result, when I change the Visible property on the WinForms host after the task returns it throws because of a cross-thread call.
The calling function looks like this:
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;
}
The code inside CreateAndInitializePreviewer does not have any async / await calls. I've verified that before the call to Task.Run() I'm always on the UI thread.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread? Any ideas are appreciated.
Upvotes: -1
Views: 403
Reputation: 456687
The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens
This is probably not going to work, unless your COM object is free-threaded (which is unlikely). If you want to push that work off your UI thread, you'll probably need to create a separate STA thread to hold the COM object and marshal calls to/from that thread - meaning all calls to that COM object, since it would live in the other STA thread. It's fairly straightforward in WPF to create a second UI (STA) thread; it's quite a bit harder but still possible in WinForms.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread?
Yes. await
will capture SynchronizationContext.Current
if it is not null
, and it should not be null
in this case. It should be either an instance of DispatcherSynchronizationContext
(which continues executing by sending a message to the WPF dispatcher) or WinFormsSynchronizationContext
(which continues executing by sending a message to the WinForms winproc).
My initial guess is that there's something odd going on with SynchronizationContext.Current
due to the WinForms-in-WPF architecture.
Upvotes: 3