c# - 如何在 C# 中使用 TPL 任务将工作编码到主线程上而不导致死锁?

标签 c# .net multithreading task-parallel-library

我正在编写一个消耗资源的库,无论出于何种原因,API 的设计方式都是在不同的线程上引发事件,但 API 的调用必须在主线程上完成。

假设我尝试使用的 API 定义为(我将省略事件定义):

public sealed class DodgyService
{
    public void MethodThatHasToBeCalledOnTheMainThread() { ... }
}

为了使用这个 API,我在我的库中添加了一个名为 Service(是的,非常原始的名称)的服务,它将创建一个新任务(它将在主线程上运行,因为我指定了一个从SynchronizationContext).

这是我的实现:

public class Service
{
  private readonly TaskFactory _taskFactory;
  private readonly TaskScheduler _mainThreadScheduler;

  public Service(TaskFactory taskFactory, TaskScheduler mainThreadScheduler)
  {
      _taskFactory = taskFactory;
      _mainThreadScheduler = mainThreadScheduler;
  }

  // Assume this method can be called from any thread.
  // In this sample is called by the main thread but most of the time
  // the caller will be running on a background thread.
  public Task ExecuteAsync(string taskName)
  {
      return _taskFactory.StartNew(
          () => ReallyLongCallThatForWhateverStupidReasonHasToBeCalledOnMainThread(taskName),
          new CancellationToken(false), TaskCreationOptions.None, _mainThreadScheduler)
          .ContinueWith(task => Trace.TraceInformation("ExecuteAsync has completed on \"{0}\"...", taskName));
  }

  private void ReallyLongCallThatForWhateverStupidReasonHasToBeCalledOnMainThread(string taskName)
  {
      Trace.TraceInformation("Starting \"{0}\" really long call...", taskName);
      new DodgyService().MethodThatHasToBeCalledOnTheMainThread();
      Trace.TraceInformation("Finished \"{0}\" really long call...", taskName);
  }

现在,如果我执行服务调用(在主线程上)并尝试在主线程上等待,应用程序将进入死锁状态,因为主线程将等待已安排在主线程上执行的任务主线程。

如何在不阻塞整个进程的情况下将这些调用编码到主线程?

在某些时候,我想在创建新任务之前执行主线程检测,但我不想破解它。

对于任何感兴趣的人,我有一个要点 here以及出现问题的代码和 WPF 应用程序。

顺便说一句,该库必须在 .net framework 4.0 上编写

编辑! 我按照 Scott Chamberlain 提供的建议解决了我的问题如提供here

最佳答案

as the main thread will be waiting for the tasks

这是一个有保证的死锁。一个任务只有在空闲时才能在主线程上执行,运行调度程序循环(也称为泵送消息循环)。正是这个调度程序循环实现了让代码在特定线程上运行的魔力。然而主线程不会空闲,它正在“等待任务”。所以任务无法完成,因为主线程不会空闲,主线程不能空闲,因为任务不会完成。死锁城。

必须重写代码,这样您的主线程就不会等待。将 wait 调用之后出现的任何代码移动到在主线程上运行的另一个任务,就像 ReallyLongCall() 一样。

请注意,您似乎根本没有从使用任务中获得任何好处,您的代码片段表明没有重要的代码在工作线程上运行。所以你还不如直接调用,问题也解决了。

关于c# - 如何在 C# 中使用 TPL 任务将工作编码到主线程上而不导致死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19917140/

相关文章:

时间:2019-03-17 标签:c#richtextboxoutofmemory

c# - 如何创建将结果返回到文本文件的 MySql 数据库连接?

c# - WebClient.DownloadFile路径问题

.net - VB.NET 默认单选按钮在组框内选中

java - Android:将另一个线程的可运行文件发布到主线程实际上有什么作用?

时间:2019-03-17 标签:c#.nettasks/threadingconsoleapplication

c# - 不支持 WPF 调用脚本?

c# - WPF+EF - 操作无法完成,因为 DbContext 已被释放

c#二进制序列化到文件逐行或如何分离

java - 如何编写线程安全操作并使它们原子化?