如何在 C# 中切换主线程和后台线程?

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

我想在后台线程中执行一些代码,并让后台代码定期在主线程中执行代码。类似于以下内容:

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);
        });
    });
}

我知道 BackgroundWorker,但它没有提供我正在寻找的灵活性。

这里有两点 -

  1. 我 'calling back' 多次到主线程 - 在执行期间两次,然后第三次执行用户提供的完成回调。
  2. 代码可读性很强。即使涉及两个线程,同步也是隐含的或在别处处理 - 如果仅从理想化的角度来看,从上到下阅读事件序列是清楚的。没有静态函数或额外的 类 可以覆盖 - 这一切都与 lambda expressions/closures.
  3. 内联

在 Obj-C 中使用 Grand Central Dispatch 很容易做到这一点(它的工作原理和上面的差不多)。是否有 C# 等效项?

您可以使用 async-await along with the Task Parallel Library:

轻松实现您的需求

此示例假定您的 MethodThatDoesStuffInBackground 或任何其他耗时方法是 CPU 绑定操作。如果不是并且他们正在做 IO,您可以放弃使用 Task.Run:

(此方法应从 UI 线程调用才能正常工作)

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

    FunctionThatMustRunOnMainThread();

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

    FunctionThatMustRunOnMainThread();

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

    FunctionThatMustRunOnMainThread();
}

我建议使用 Task.Run 进行后台工作,IProgress<T> 进行进度更新,Task 进行完成通知。这种方法使您能够将后台逻辑放在一个地方,与 UI.

分开

像这样:

// 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!");
}

请注意,completionAction 不再是必需的,因为 launchBackgroundTaskAsync 本身 returns 一个 Task。它可以简单地删除而不会损失任何功能 - 只需让此方法的 callers 使用 await:

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

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