Может ли параллельная библиотека задач .NET 4 использовать объекты COM?

Это "возможно ли это, и если да, то можете ли вы дать мне быстрый пример, потому что я не могу найти его в Интернете?" вопрос.

У меня есть несколько полностью отдельных (т.е. "смущающих параллельных" ) процессов, которые я хочу запускать параллельно, используя библиотеку задач Parallel в .NET Framework 4 с использованием С#. Некоторые из этих процессов требуют использования программного обеспечения, к которому можно получить доступ через автоматизацию COM/OLE.

В частности, существует цикл Parallel.Foreach(), который делит задачи из списка элементов, в основном вызывающий другую функцию внутри Parallel.Foreach для обработки обработки (поэтому некоторые из этих функций используют COM-библиотеки для работы).

Возможно ли это? Спасибо.

Ответ 1

На 100% можно использовать COM-объекты с TPL. Хотя верно, что по умолчанию TPL будет использовать стандартный .NET ThreadPool, TPL имеет точку расширения через класс TaskScheduler, который позволяет вам предоставить свой собственный планировщик, который может отправлять работу на созданные вами потоки.

В случае использования COM-объектов вам сначала нужно знать, требуется ли COM-класс для поточной передачи STA или поточной передачи MTA. Если MTA threading, то нет ничего особенного, что нужно сделать, потому что COM-класс уже может использоваться из любой случайной нити. К сожалению, большинство классических COM-объектов, как правило, полагаются на потоки STA и что, когда вам нужно использовать пользовательский TaskScheduler, так что любой поток .NET, из которого вы их используете, был инициализирован как совместимый с STA поток.

В то время как TaskSchedulers не совсем тривиально писать, на самом деле их не так сложно написать, если у вас есть базовое понимание потоков. К счастью, библиотека 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);

Ответ 2

Это потенциально возможно, но оно также может не работать.

Для многих COM-объектов требуется конкретная перенос квартиры. Когда вы используете Parallel.For/ForEach, вы работаете в .NET ThreadPool, который не имеет настройки потоковой передачи квартиры. Это может работать и может использоваться для некоторых COM-объектов, но также может вызывать сбои и странные COM-исключения, которые трудно отследить.

Ответ 3

Некоторая дополнительная информация, которую я еще не проверял, но это может быть полезно. Планировщик заданий по умолчанию будет использовать текущий поток для выполнения некоторой части работы, а затем добавлять дополнительные потоки из пула потоков по мере необходимости.

Это может вызвать проблемы, если вы используете COM-объект при выполнении Parallel.ForEach. Например, пусть ваш основной поток - STA. Вы создаете экземпляр COM-объекта и используете Parallel.ForEach для выполнения некоторой работы, когда каждый поток пытается получить доступ к ранее создаваемому COM-объекту. Я подозреваю, что он сломается, и первоначальное тестирование, похоже, подтверждает это. В этом случае я вижу по крайней мере пару вариантов:

  • Предполагая, что объект COM поддерживает MTA, используйте вызывающий поток MTA. Однако это может быть не вариант по другим причинам. Например, если приложение является приложением Windows Forms, я считаю, что Main() должен иметь атрибут STAThread.
  • Используйте альтернативный планировщик задач, такой как StaTaskScheduler, упомянутый Дрю. Вы можете либо иметь все потоки STA, либо использовать планировщик, который не использует вызывающий поток, и запускать все потоки MTA.