Может ли параллельная библиотека задач .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.