【问题标题】:Run tasks serially on one thread在一个线程上串行运行任务
【发布时间】:2020-12-03 07:41:30
【问题描述】:

我有几个线程(做服务器工作)需要排队在主线程上完成工作(他们需要访问只能从主线程调用的 API)。

使用任务系统对我来说很有意义,因为服务器线程可以生成使用这个仅主线程 API 的任务,将它们排队以在主线程上运行并等待它们。主线程可以旋转,从 ConcurrentQueue 中弹出任务并在它们进入时运行它们。

从本质上讲,这有点与通常的模式相反,其中任务在线程池中并行运行。在这里,任务是从多个其他线程创建的,并排队等待在一个主线程上运行。

现在有一个问题:其中一些任务可能会产生子任务,这些子任务本身需要在主线程上运行。例如:

# On server thread:
Task taskA = new Task(async () => {
  Task taskB = new Task(() => Console.WriteLine("some sub work"));
  MainThreadWorkQueue.BeginInvoke(taskB);
  await taskB;
});
MainThreadWorkQueue.BeginInvoke(taskA);
await taskA;

这可能是因为顶级任务之一是使用库代码,该库代码本身需要在主线程上运行。

所以我最终得到了一个树状结构的任务,其中根级任务需要阻止等待叶任务完成,但根任务也在执行时动态创建这些叶任务。

问题是我不确定如何编写主线程代码。我在想这样的事情:

// Main thread:
while(true) {
  Task currentTask;
  while (MainThreadWorkQueue.TryDequeue(out currentTask)) {
    currentTask.Wait();
  }

  // Do other work
}

但是,这将在上面的 taskA 示例中死锁,因为 taskB 排队但 taskA 永远不会完成以允许 taskB 的工作开始。

理想情况下,我想做这样的事情:

// Main thread:
while(true) {
  Task currentTask;
  while (MainThreadWorkQueue.TryDequeue(out currentTask)) {
    currentTask.RunUntilFinishedOrBlocked();
    if (currentTask.IsBlockedOnOtherTask) {
      MainThreadWorkQueue.Enqueue(currentTask)
    }
  }

  // Do other work
}

有没有办法运行一个任务,直到它被另一个任务阻塞?或者用不同的方法来解决这个问题?

【问题讨论】:

标签: c# async-await task task-parallel-library


【解决方案1】:

You should never use the Task constructor。它的用例几乎为零。

在您的情况下,您要查找的类型不是 Task;这是一个代表:

我正在尝试使用 Tasks 来表示工作单元

Task 是不正确的类型。听起来更像Func<Task>。

我想运行 taskA 直到它阻塞,然后运行 ​​taskB,然后继续运行 taskA。

在这种情况下,您应该使用单线程SynchronizationContext。您可以通过在 UI 线程上运行这些任务,或者通过在类似AsyncContext 中运行它们来实现这一点。 AsyncContext 有一个内置的工作队列,它会一直处理直到完成。

【讨论】:

  • 您能否扩展您的答案以解释您的陈述的“为什么/如何”?例如:为什么我不应该使用 Task 构造函数?为什么/如何委托比任务更好?等
  • @JayLemmon:我添加了一个博客链接,因为完整的描述会很长。
  • 你能扩展最后一段,关于 SynchronizationContext 吗?这听起来像我想要的,但我不确定在代码中会是什么样子。
  • @JayLemmon: Here's some examples.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 1970-01-01
  • 2014-09-17
  • 1970-01-01
相关资源
最近更新 更多