.NET 4 任务并行库可以使用 COM 对象吗?

发布于 2024-10-17 04:13:57 字数 289 浏览 8 评论 0原文

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

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

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

这可能吗?谢谢。

This is an "is this possible, and if so can you give me a quick example because I can't find one online?" kind of question.

I have a number of completely separate (i.e. "embarrassingly parallel") processes that I want to run in parallel using the Task Parallel library in .NET Framework 4 using C#. Some of these processes require the use of software that can be accessed via COM/OLE automation.

Specifically, there is a Parallel.Foreach() loop that divides up the tasks from a list of items, basically calling a different function inside the Parallel.Foreach to handle the processing (so some of these functions employ COM libraries to work).

Is this possible? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不疑不惑不回忆 2024-10-24 04:13:57

将 COM 对象与 TPL 结合使用是 100% 可能的。虽然默认情况下 TPL 确实将使用标准 .NET ThreadPool,但 TPL 通过 TaskScheduler,它使您能够提供自己的调度程序,该调度程序可以将工作分派到您创建的线程。

在使用 COM 对象的情况下,您首先需要知道 COM 类是否需要 STA 线程或 MTA 线程。如果是 MTA 线程,则无需执行任何特殊操作,因为 COM 类已可以从任何随机线程中使用。不幸的是,大多数经典的 COM 对象往往依赖于 STA 线程,此时您需要使用自定义 TaskScheduler ,以便您使用它们的任何 .NET 线程都已 初始化为 STA 兼容线程

虽然 TaskScheduler 的编写并不简单,但如果您对线程有基本的了解,那么编写它们也并不难。幸运的是,ParallelExtensions Extras 库 已经提供了一个 StaTaskScheduler 类,因此您不需要甚至不需要自己写任何东西。 PFX 这里有一篇很棒的博客文章团队讨论 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);

It's 100% possible to use COM objects with the TPL. While it's true that, by default, the TPL will use the standard .NET ThreadPool, the TPL has an extension point via the TaskScheduler class which enables you to provide your own scheduler which can dispatch work to threads which you've created.

In the case of of using COM objects you first need to know if the COM class requires STA threading or MTA threading. If MTA threading, then there's nothing special that needs to be done because the COM class can already be used from any random thread. Unfortunately most classic COM objects tend to rely on STA threading and that's when you'd need to employ a custom TaskScheduler so that whatever .NET thread you're using them from has been initialized as an STA compatible thread.

While TaskSchedulers are not exactly trivial to write, they're not really that hard to write either if you've got a basic understanding of threading. Luckily the ParallelExtensions Extras library already provides an StaTaskScheduler class so you don't even need to write anything yourself. There's a great blog post here by the PFX team that discusses the implementation of and some use cases for the the StaTaskScheduler class.

Basically though, you'll want to initialize a new StaTaskScheduler as a static somewhere on one of your classes and then just start your Tasks specifying that they are scheduled by that instance. That would look something like this:

// 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);
反话 2024-10-24 04:13:57

这有可能,但也可能行不通。

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

It's potentially possible, but it may also not work.

Many COM objects require a specific apartment threading. When you use Parallel.For/ForEach, you're running on the .NET ThreadPool, which doesn't have the apartment threading setup. This may work, and can for some COM objects, but can also cause crashes and strange COM exceptions that are difficult to track down.

寂寞笑我太脆弱 2024-10-24 04:13:57

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

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

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

Some additional information that I am yet to verify, but that may be helpful. The default task scheduler will use the current thread to do some of the work then add additional threads from the thread pool as necessary.

This could cause problems if you are sharing a COM object when doing Parallel.ForEach. For example, let's say your main thread is STA. You instantiate the COM object on that and use Parallel.ForEach to do some work where each thread tries to access the previously instantiated COM object. I suspect that it will break, and initial testing seems to back this up. In this scenario I see at least a couple of options:

  • Assuming the COM object supports MTA, have the calling thread use MTA. However this may not be an option for other reasons. For example, if the application is a Windows Forms application I believe Main() is required to have the STAThread attribute.
  • Use an alternative task scheduler such as the StaTaskScheduler mentioned by Drew. You could either have all STA threads or use a scheduler that does not use the calling thread and run all MTA threads.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文