Reputation: 4534
I've a strange behavior on my WPF window. To summarize, I have a WPF window that do an async stuff on loaded event
if (AppContext.OnlineMode)
Task.Run(() => SynchronizeMails());
This function (synchronizeMails), do a lot of stuff (async contacting webservice, insert in database, refresh GUI, ...), and on first launch, it take a little long time.
I have a button that allow user to disconnect, bound to a command that show so messageBox depending of the current state. For my case, synchronizeMails set a bool to true, to prevents multiple synchronization and to prevents exit during treatment. My command implementation look at this boolean, and show a messageBox if is currently synchronizing.
ExitCommand = new RelayCommand(p => Task.Run(() =>
{
RestartAsked = true;
if (Synchronizing)
{
OpeningView.ShowWarning("...");
}
});
We have, for style reason, recoded message boxes in our own implementation, so the call to messageBox ShowWarning is only a ShowDialog on the main GUI, with no async/await stuff.
The strange is comming here : when user click OK on this message box, my async method just stop doing it's job, execute the finaly bloc (whole method is in a try-finally block to disable my boolean if there's some errors), and of course, the job isn't finished.
I don't understand why the showDialog with return make my async method stopping...
Does anyone have an idea ?
Upvotes: 1
Views: 289
Reputation: 456417
I've found that it was because my async stuff were in Parallel.For and Parallel.ForEach, that make it return before end
Parallelism and asynchrony are two different forms of concurrency, and they don't really play well together.
Parallelism should be used when you have CPU-bound algorithms that would benefit from multiple cores. Asynchrony should be used when you have I/O-bound or event-triggered code.
async contacting webservice, insert in database, ...
Sounds like asynchrony is the appropriate form of concurrency, not parallelism.
To properly apply asynchrony, start at the "leaves" (i.e., the code doing the webservice call and the code inserting in the database), and change that code to use asynchronous APIs (with await
). Since you're using await
, you'll have to change that method to be async
and change its return type to Task
/Task<T>
. Then all the callers of that method need to use await
, and become async
, etc.
Eventually, you'll end up with a proper SynchronizeMailsAsync
method that is truly asynchronous, and can be called from your loading event handler as such:
if (AppContext.OnlineMode)
await SynchronizeMailsAsync();
Note that Task.Run
is not necessary. Task.Run
should only be used to push CPU-bound work off the UI thread, and your work is I/O-bound.
Now, at the point in your code (which isn't shown) that you used to have parallelism, you can make it concurrent by using Task.WhenAll
. So instead of:
Parallel.ForEach(sequence, x => MyAsync(x));
you should do:
var tasks = sequence.Select(x => MyAsync(x));
await Task.WhenAll(tasks);
Upvotes: 3
Reputation: 7944
To detect whether there are unhandled exceptions being thrown in your Task
objects during Task.Run
you should handle the TaskScheduler.UnobservedTaskException
event.
If there are exceptions being thrown in other parts of your code contained in the Task
, this will identify them and their source.
Upvotes: 0