简体   繁体   中英

How can I switch between main and background thread in C#?

I'd like to execute some code in a background thread, and have the background code periodically execute code on the main thread. Something like the following:

void functionThatMustBeRunOnMainThread(string arg) {
    Debug.Print("On main thread: "+arg);
}

// Run the task asynchronously. On completion, call completionAction in the main thread.
void launchAsyncBackgroundTask( Action<bool> completionAction ) {
    performInArbitraryBackgroundThread(() => {
        // do first long running thing
        sleep(10);

        // notify main thread
        performOnMainThread(() => {
            functionThatMustBeRunOnMainThread("phase 1");
        });

        // do second long running thing
        sleep(10);

        // notify main thread
        performOnMainThread(() => {
            functionThatMustBeRunOnMainThread("phase 2");
        });

        // do final long running thing
        sleep(10);

        performOnMainThread(() => {
            Debug.Print("Done!");
            completionAction(true);
        });
    });
}

I know about BackgroundWorker but it doesn't offer the flexibility I'm looking for.

There's two points here -

  1. I'm 'calling back' to the main thread multiple times - twice during execution, then a third time to execute the user-supplied completion callback.
  2. The code is very readable. Even though two threads are involved, synchronization is implicit or handled elsewhere - reading top to bottom the sequence of events is clear, if only from an idealized perspective. There are no static functions or extra classes to override - it's all happening inline with lambda expressions/closures.

This is trivial to do in Obj-C using Grand Central Dispatch (it pretty much works as above). Is there a C# equivalent?

You can achieve what you need quite easily using async-await along with the Task Parallel Library :

This example assumes that your MethodThatDoesStuffInBackground or any of the other time-consuming methods are CPU bound operations. If not and they're doing IO, you can drop the use of Task.Run :

(This method should be called from the UI thread in order to work properly)

public async Task DoStuff()
{
    await Task.Run(() => MethodThatDoesStuffInBackground());

    FunctionThatMustRunOnMainThread();

    await Task.Run(() => MethodThatDoesMoreStuffInBackground());

    FunctionThatMustRunOnMainThread();

    await Task.Run(() => EvenMoreWorkInBackgroundThread());

    FunctionThatMustRunOnMainThread();
}

I recommend using Task.Run for background work, IProgress<T> for progress updates, and Task for completion notification. This approach enables you to keep your background logic in one place, separate from the UI.

Something like this:

// Run the task asynchronously. On completion, call completionAction in the main thread.
async Task launchBackgroundTaskAsync( Action<bool> completionAction ) {
  var progress = new Progress<string>(arg => {
      Debug.Print("On main thread: "+arg);
  };

  await Task.Run(() => BackgroundLogic(progress));
  completionAction(true);
}

void BackgroundLogic(IProgress<string> progress) {
  // do first long running thing
  sleep(10);

  // notify main thread
  if (progress != null)
    progress.Report("phase 1");

  // do second long running thing
  sleep(10);

  // notify main thread
  if (progress != null)
    progress.Report("phase 2");

  // do final long running thing
  sleep(10);

  if (progress != null)
    progress.Report("Done!");
}

Note that the completionAction is no longer necessary, since launchBackgroundTaskAsync itself returns a Task . It can simply be removed without any loss of capability - just have the callers of this method use await :

async Task launchBackgroundTaskAsync() {
  var progress = new Progress<string>(arg => {
      Debug.Print("On main thread: "+arg);
  };

  await Task.Run(() => BackgroundLogic(progress));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM