0

So I'm trying to use task progress notification using IProgress<T>. Here is what I do:

  1. Create a Progress<T> and attach a handler to its ProgressChanged event.
  2. In the event handler, I update UI-specific property to show progress.
  3. Run a loop and create Task objects to do units of work. Each Task uses Progress object to report progress (which will in turn invoke ProgressChanged and update UI).
  4. Use Task.WaitAll() to block till all tasks have finished. Show a completion message at the end.

The code looks something like this:

IProgress<Tuple<DataRow, MyVM>> BakeProgress;
BakeProgress = new Progress<Tuple<DataRow, MyVM>>();

((Progress<Tuple<DataRow, MyVM>>)BakeProgress).ProgressChanged += (sender, args) =>
{
  _BakeProgress += 100.0 / AllDataRows.Length;
  RaisePropertyChanged(NameOf(BakeProgress));
};

var BakeTasks = new List<Task>();
foreach (var dr in AllDataRows) {

  BakeTasks.Add(Task.Run(() =>
  {
    var Baked = MyBakingFunc();

    BakeProgress.Report(new Tuple<DataRow, MyVM>(dr, Baked));

    return Baked;
  }));
}

Task.WaitAll(BakeTasks.ToArray());      //Shouldn't this wait

MessageBox.Show("Baking Completed");

To my surprise, this code hits MessageBox line before hitting ProgressChanged event handler even once. What am I doing wrong?

N.B. It DOES hit ProgressChanged event handler once for every Task instance, but only after hitting the Completed line.

N.B. Ah and I did read this, but since this is WPF, we do have a synchronization context in which the Progress object was created.

dotNET
  • 33,414
  • 24
  • 162
  • 251
  • 2
    Tip: You don't need `Task.Run`, and you should use `await Task.WhenAll` instead of `Task.WaitAll`. – Dai Apr 16 '18 at 17:42
  • Also, please use `camelCase` for locals and parameters, not `PascalCase`. – Dai Apr 16 '18 at 17:43
  • @Dai: Thanks. Why don't I need `Task.Run`? Are you suggesting I shouldn't multi-thread it? – dotNET Apr 16 '18 at 17:53
  • 1
    As you read - progress handler will run on UI thread in your case. But your UI is blocked waiting for all tasks to complete. So only after all tasks are complete (and message box shown) - UI thread will be available to run progress handlers. – Evk Apr 16 '18 at 17:54
  • @Dai: I do use camelCase for parameters, but PascalCase for locals. Not sure exactly when I started following that style, but it does help me at times. – dotNET Apr 16 '18 at 17:54
  • That depends if `MyBakingFunc` is IO-bound or not. If it's IO-bound then you don't need more threads, you can do async programming with a single thread. If it's CPU bound then yes, use `Task.Run`. – Dai Apr 16 '18 at 17:55
  • @Evk: aaaaaaaaaah... – dotNET Apr 16 '18 at 17:58
  • @Dai: I see it now. And thanks, `WhenAll()` solves this issue. And no, my tasks are not IO-bound. – dotNET Apr 16 '18 at 17:59

0 Answers0