Can the .NET 4 Task Parallel Library use COM objects?
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 (ie "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.
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);
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.
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:
上一篇: C#平行foreach同样完成任务