【问题标题】:How can a child thread wait for something in the main thread to complete?子线程如何等待主线程中的某些内容完成?
【发布时间】:2017-08-16 13:14:05
【问题描述】:

通常问题是主线程等待子线程,我可以在Task 上使用await

但我有一种情况,子线程必须等待主线程中的某个方法完成才能继续。如何让这种交流成为可能?

public void MainThread()
{
    Task t = Task.Run(ChildThread); //not using await because we want to continue doing work that must be only on the main thread
    WorkA();
    WorkB();
    ???
    WorkC();
}

public async Task ChildThread()
{
    WorkD();
    ???
    Need_WorkB_ToBeCompletedBeforeThisOne();
}

WorkA WorkB WorkC 都必须在主线程上。在我的真实代码中,所有这些都是间接从子线程发出的,因为子线程需要它们完成但由于线程限制而无法完成。

我有办法让我的子线程告诉主线程已经开始工作(所以我将代码简化为您在此处看到的内容,一旦任务开始,工作将立即进行)但我需要一些方法发布者(子线程)了解这些命令的进度。

PS。感谢来自这个线程的答案,我为 Unity 创建了 CoroutineHost,您可以使用 await 可以从任何子线程向主线程发出的 yield IEnumerator 方法:https://github.com/5argon/E7Unity/tree/master/CoroutineHost

【问题讨论】:

  • 为什么不让ChildThread() 只做WorkD() 并返回,然后在MainThread() 中使用await WorkB() 并在此之后调用Need_WorkB...()
  • 我的错,实际上 WorkA 到 C 将由 ChildThread() 发出(因为如果 WorkA 到 C 在主线程以外的线程上执行,则存在软件限制)我想要一种方法在问题之后,ChildThread() 可以得到某种关于主线程中事情进展的指示。

标签: c# multithreading task-parallel-library


【解决方案1】:

您正在寻找“信号”。由于子进程是异步的并且信号只使用一次,TaskCompletionSource<T> 会很好地工作:

public void MainThread()
{
  var tcs = new TaskCompletionSource<object>(); // Create the signal
  Task t = Task.Run(() => ChildThread(tcs.Task)); // Pass the "receiver" end to ChildThread
  WorkA();
  WorkB();
  tcs.SetResult(null); // Send the signal
  WorkC();
}

public async Task ChildThread(Task workBCompleted)
{
  WorkD();
  await workBCompleted; // (asynchronously) wait for the signal to be sent
  Need_WorkB_ToBeCompletedBeforeThisOne();
}

有关更多信息,请参阅my book 中的配方 11.4“异步信号”。如果您需要一个可以多次设置和取消设置的“信号”,那么您应该使用AsyncManualResetEvent

【讨论】:

  • 不知道为什么Task.Run 包裹着 ChildThread,你能解释一下吗?
  • @CamiloTerevinto:在问题代码中。我认为Task.Run 是必要的,因为ChildThread 必须在不同的线程中运行。
  • 那么,这意味着如果你不使用Task.Run 包裹它就不会在不同的线程上运行?一些帖子给我的印象是,它是 async Task,它已经在另一个线程上运行了
  • @CamiloTerevinto:不,async 不会在不同的线程上运行代码。
【解决方案2】:

如果 WorkB 是一个任务,我会想象这样的事情:

public void MainThread()
{
    Task workB = WorkB();
    ChildThread(workB); // you don't need to use Task.Run here, the task is ran automatically
    WorkA();
    WorkC();
}

public async Task ChildThread(Task otherTaskToWait)
{
    WorkD();
    await otherTaskToWait;
    Need_WorkB_ToBeCompletedBeforeThisOne();
}

【讨论】:

  • 谢谢,但WorkB由于程序限制(Unity)不能在其他线程上。如果WorkB 真的被Task workB = WorkB(); 变成了一个任务,那是否意味着我们已经把它放到了另一个线程中? (所以我们可以等待它)
  • @5argon 如果您使用的是 Unity,您使用的是 2017 实验版吗?否则,Unity 不知道 Task 的。我假设 workb 可以在不同的线程上运行
  • @5argon 一个操作是异步的并不意味着它正在使用另一个线程。在另一个线程中运行代码只是使操作异步的一种方式。
  • @5argon 不管现在有什么问题,我建议你等一等,因为我发现 Unity 将来可能会允许 UnityWebRequest 在不同的线程上运行(否则,为什么甚至允许不同的线程线程...?)Stephen Cleary 的答案就是您正在寻找的答案
  • 哦,我刚刚在stackoverflow.com/questions/27891253/… 中阅读了有关手动创建自己的Task 的信息,并意识到这实际上是 Stephen Cleary 的答案。现在我觉得很好。 (有了这个,实际上这个答案几乎等于斯蒂芬的答案,通过使WorkB 的内部产生TaskCompletionSource -> Task
猜你喜欢
  • 2012-07-22
  • 1970-01-01
  • 2022-01-02
  • 2017-07-19
  • 2016-04-11
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多