Reputation: 19930
I have an application with a startup window (in WPF) which lets the user log in, choose a database, make some other settings, and eventually (upon success) shows a progress bar. When the progress is done, a main window (in WinForms) is shown instead.
To make the progress bar update smoothly, I'm separating as much as I can away from the UI thread. Here's the current approach:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundScheduler = TaskScheduler.Default;
var connectingTask = Task.Factory.StartNew(() =>
{
System.Diagnostics.Debug.WriteLine("connecting started");
// some code that connects to a server; may fail
System.Diagnostics.Debug.WriteLine("connecting finishing");
}
var connectFailAction = new Action<Task>((t) =>
{
System.Diagnostics.Debug.WriteLine("connectFail started");
// let user know that connecting failed, then exit
System.Diagnostics.Debug.WriteLine("connectFail finishing");
});
var postConnectAction = new Action<Task>((t) =>
{
System.Diagnostics.Debug.WriteLine("postConnect started");
// some initial post-connection setup code; runs in UI thread
System.Diagnostics.Debug.WriteLine("postConnect finishing");
});
var dbLoadingAction = new Action<Task>((t) =>
{
System.Diagnostics.Debug.WriteLine("dbLoading started");
// code that loads settings from the database; runs in background
System.Diagnostics.Debug.WriteLine("dbLoading finishing");
});
var runGuiAction = new Action<Task>((t) =>
{
System.Diagnostics.Debug.WriteLine("runGui started");
// set up the main form, show it, hide this startup progress window
System.Diagnostics.Debug.WriteLine("runGui finishing");
});
connectingTask.ContinueWith(connectFailAction,
System.Threading.CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
uiScheduler);
connectingTask.ContinueWith(postConnectAction,
System.Threading.CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
uiScheduler)
.ContinueWith(dbLoadingAction,
System.Threading.CancellationToken.None,
TaskContinuationOptions.None,
backgroundScheduler)
.ContinueWith(runGuiAction,
System.Threading.CancellationToken.None,
TaskContinuationOptions.None,
uiScheduler);
If the connection fails, the task goes straight to connectFailAction
.
Otherwise, on the UI thread, postConnectAction
is run next, while simultaneously, dbLoadingAction
is run in the background. Afterwards, runGuiAction
is run.
This mostly works fine, but one odd edge case where it breaks down is CefSharp
. Specifically, non-CefSharp portions of the UI are 'stuck' — I can't resize the form, I can't interact with any controls, etc.
Since equivalent code works fine with the WinForms WebBrowser
control, I'm guessing it's partially an issue with CefSharp, but part of it may be my incorrect use/understanding of the TPL.
If I change the code as follows, the UI gets stuck during loading (unsurprisingly), but CefSharp inits correctly.
connectingTask.ContinueWith(connectFailAction,
System.Threading.CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
uiScheduler);
connectingTask.Wait();
postConnectAction.Invoke(null);
dbLoadingAction.Invoke(null);
runGuiAction.Invoke(null);
If (and only if) using CefSharp, the main thread is stuck in System.Windows.Threading.DispatcherSynchronizationContext.Wait
, so I'm guessing one of the above tasks never cleanly finishes, causing an event queue not to flush properly?
Upvotes: 1
Views: 282
Reputation: 149538
The main thread is stuck in
System.Windows.Threading.DispatcherSynchronizationContext.Wait
, so I'm guessing one of the above tasks never cleanly finishes, causing an event queue not to flush properly?
The problem is your code deadlocks in the latter example. You're invoking the Task
which later attempts to run the continuation on the UI thread, which is currently blocked by connectingTask.Wait();
. This is why you shouldn't block on async code.
Regarding CefSharp, perhaps the continuation you're running on the UI thread after connecting is CPU intensive and causing your UI message loop to halt for more than a reasonable amount of time. Can't be sure since you haven't provided the relevant code.
Upvotes: 1