简体   繁体   中英

Why would await Task.Run() not return to the same thread?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM