16

As i don' know about threads much i have a question. I wanna do something in background and in background method i wanna switch back to the main thread on certain condition otherwise work in background. How can i achieve this functionality? I am using a call to StartSyncThread from UI class(c#)

async void StartSyncThread()
{
    await DoSyncAsync();
}

Task DoSyncAsync()
{
    return Task.Run(() => DoSync());            
}

in DoSync method i wanna switch back to main thread so that i can change UI. Please give me a simple solution to do this. Thanks in advance!

Shulhi Sapli
  • 2,286
  • 3
  • 21
  • 31
Jatin
  • 462
  • 1
  • 6
  • 19

2 Answers2

12

First start your async process, then call Dispatcher.BeginInvoke to get back on the UI thread.

Task.StartNew(() =>
{
   // Do Something Async

   Dispatcher.BeginInvoke(() =>
   {
      // Update Your UI Here 
   });
});

Note that Dispatcher is not a static - this relies on your code being a part of a member function for a UI object, like a method on your page object.

Deeko
  • 1,514
  • 9
  • 29
9

There are a couple of approaches.

The first is to split up the synchronous method into different parts. This is best if your synchronous method calculates different types of things that go into different parts of the UI:

async Task DoSyncAsync()
{
  myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
  myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}

The second is to use progress reporting. This is best if your UI updates are all of the same type:

Task DoSyncAsync()
{
  Progress<MyProgressType> progress = new Progress<MyProgressType>(progressUpdate =>
  {
    myDataBoundUIProperty = progressUpdate;
  });
  return Task.Run(() => DoSync(progress));
}
void DoSync(IProgress<MyProgressType> progress)
{
  ...
  if (progress != null)
    progress.Report(new MyProgressType(...));
  ...
}

There is a final alternative, but I strongly recommend one of the two above. The two solutions above will result in a better code design (separation of concerns). The third alternative is to pass in a TaskFactory that can be used to run arbitrary code on the UI context:

Task DoSyncAsync()
{
  var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
  var factory = new TaskFactory(scheduler);
  return Task.Run(() => DoSync(factory));
}
void DoSync(TaskFactory factory)
{
  ...
  scheduler.StartNew(() => { ... });
  ...
}

Again, I don't advise this last solution since it conflates your UI update logic with your background task logic. But it's better than using Dispatcher or Control directly.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810