1

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 ?

cdie
  • 4,014
  • 4
  • 34
  • 57
  • Is the bool set inside your SyncronizeMails method? Try setting the bool to true before you call your method. – wingerse Oct 01 '15 at 07:53
  • Yes, it is, but moving it don't change anything – cdie Oct 01 '15 at 08:00
  • It looks like some exceptions are thrown inside your `SynchronizeMails` method. Try putting `catch(){;}` before your finally block, maybe there are some InvalidOperation or SynchronizeContext stuff happening. – maiksaray Oct 01 '15 at 08:01
  • It was my first thought, but no, no exceptions is thrown. I put a break point in the end of the method, on the last instructions, and after "ok" is clicked, my breakpoint fire, whereas my async stuff inside the method is not yet finished ... – cdie Oct 01 '15 at 08:07
  • I've found that it was because my async stuff were in Parallel.For and Parallel.ForEach, that make it return before end ... Changing Parallel.For by for and Parallel.ForEach by foreach make it works. However, I don't know how to parallelize job, cause it make it slower now ! – cdie Oct 01 '15 at 08:13

2 Answers2

3

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);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

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.

toadflakz
  • 7,764
  • 1
  • 27
  • 40