【问题标题】:Can the .NET 4 Task Parallel Library use COM objects?.NET 4 任务并行库可以使用 COM 对象吗?
【发布时间】:2012-07-02 12:31:23
【问题描述】:

这是一个“这可能吗?如果可以,你能给我一个简单的例子吗,因为我在网上找不到一个?”类似的问题。

我有许多完全独立的(即“令人尴尬的并行”)进程,我想使用 .NET Framework 4 中的 Task Parallel 库使用 C# 并行运行这些进程。其中一些过程需要使用可通过 COM/OLE 自动化访问的软件。

具体来说,有一个 Parallel.Foreach() 循环从项目列表中划分任务,基本上调用 Parallel.Foreach 内部的不同函数来处理处理(因此其中一些函数使用 COM 库来工作)。

这可能吗?谢谢。

【问题讨论】:

  • 听起来是快速测试的完美候选者......试试吧。

标签: c# com .net-4.0 ole task-parallel-library


【解决方案1】:

在 TPL 中使用 COM 对象是 100% 可能的。虽然默认情况下,TPL 将使用标准的 .NET 线程池,但 TPL 有一个通过 the TaskScheduler class 的扩展点,它使您能够提供自己的调度程序,该调度程序可以将工作分派到您创建的线程。

在使用 COM 对象的情况下,您首先需要知道 COM 类是否需要 STA 线程或 MTA 线程。如果是 MTA 线程,则不需要做任何特别的事情,因为 COM 类已经可以从任何随机线程中使用。不幸的是,大多数经典 COM 对象往往依赖于 STA 线程,这时您需要使用自定义 TaskScheduler,以便您使用它们的任何 .NET 线程都是 initialized as an STA compatible thread

虽然 TaskScheduler 编写起来并不简单,但如果您对线程有基本的了解,编写它们也不是那么难。幸运的是 the ParallelExtensions Extras library 已经提供了一个 StaTaskScheduler 类,所以你甚至不需要自己写任何东西。 PFX 团队的a great blog post here 讨论了StaTaskScheduler 类的实现和一些用例。

不过,基本上,您需要在某个类的某处将新的StaTaskScheduler 初始化为静态,然后只需启动您的Tasks,指定它们是由该实例安排的。看起来像这样:

// Create a static instance of the scheduler specifying some max number of threads
private static readonly StaTaskScheduler MyStaTaskScheduler = new StaTaskScheduler(4);

....

// Then specify the scheduler when starting tasks that need STA threading
Task.TaskFactory.StartNew(
() =>
{
    MyComObject myComObject = new MyComObject();

    myComObject.DoSomething();

    // ... etc ...
},
CancellationToken.None,
TaskCreationOptions.None,
MyStaTaskScheduler);

【讨论】:

  • 非常有趣,我得研究一下,谢谢!作为参考,我试图实现的主要功能是在 R (r-project.org) 中并行执行多个工作流。如果我发现了什么,我会在这里报告。
  • 谢谢,正是我需要的。
  • StaTaskScheduler 类的代码示例链接已过时。当前是here
【解决方案2】:

这是可能的,但也可能行不通。

许多 COM 对象需要特定的apartment threading。当您使用 Parallel.For/ForEach 时,您在 .NET ThreadPool 上运行,它没有单元线程设置。这可能有效,并且适用于某些 COM 对象,但也可能导致崩溃和难以追踪的奇怪 COM 异常。

【讨论】:

    【解决方案3】:

    我尚未验证的一些附加信息,但这可能会有所帮助。默认任务调度器将使用当前线程来完成一些工作,然后根据需要从线程池中添加额外的线程。

    如果您在执行 Parallel.ForEach 时共享 COM 对象,这可能会导致问题。例如,假设您的主线程是 STA。您在其上实例化 COM 对象并使用 Parallel.ForEach 执行一些工作,其中每个线程都尝试访问先前实例化的 COM 对象。我怀疑它会坏掉,初步测试似乎支持了这一点。在这种情况下,我至少看到了几个选项:

    • 假设 COM 对象支持 MTA,让调用线程使用 MTA。但是,由于其他原因,这可能不是一种选择。例如,如果应用程序是 Windows 窗体应用程序,我认为 Main() 需要具有 STAThread 属性。
    • 使用替代任务调度程序,例如 Drew 提到的 StaTaskScheduler。您可以拥有所有 STA 线程,也可以使用不使用调用线程并运行所有 MTA 线程的调度程序。

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多