QueueUserWorkItem с COM в C++

У меня проблема с производительностью, когда клиенты создают сотни объектов определенного типа "Foo" в DOM моего приложения C++. У каждого экземпляра Foo есть собственная асинхронная рабочая очередь с собственным потоком. Очевидно, что это не масштабируется.

Мне нужно разделить потоки между рабочими очередями, и я не хочу изобретать велосипед. Мне нужно поддерживать XP, поэтому я не могу использовать пул потоков Vista/Win7. Работа, которую необходимо выполнить для обработки каждого элемента очереди, включает в себя вызовы COM в многопоточном апартаменте COM. В документации для пула потоков XP говорится, что можно вызывать CoInitializeEx() с апартаментом MTA в обратном вызове функции рабочего потока. Я написал тестовое приложение и убедился, что оно работает. Я заставил приложение выполнить 1 миллион итераций с парой CoInitializeEx/CoUninitialize и без нее в функции обратного вызова WorkItem. Это занимает 35 секунд с вызовами CoInit* и 5 секунд без них. Это слишком много накладных расходов для моего приложения. Поскольку пул потоков предназначен для каждого процесса, а сторонний код выполняется в моем процессе, я предполагаю, что небезопасно использовать CoInitializeEx() один раз для каждого потока и никогда не использовать CoUninitialize().

Учитывая все это, могу ли я каким-либо образом использовать пул потоков Win32? Я что-то упустил, или пул потоков XP совершенно бесполезен для высокопроизводительных COM-приложений? Мне просто придется создать свою собственную систему разделения потоков?


person David Gladfelter    schedule 04.02.2010    source источник


Ответы (2)


Вы проверили, что так долго? то есть это вызов CoInitializeEx()? Вам определенно не нужно вызывать CoInitialize один раз для каждой задачи. Вы также не говорите, сколько потоков вы создаете, т. е. если вы работаете на двухъядерном процессоре и ваша работа интенсивно использует процессор, не ожидайте ускорения более чем в 2 раза, а если ваша работа нет Интенсивность ЦП, то он ожидает на каком-то ресурсе (памяти, диске, сети), и ускорение будет аналогичным образом ограничено, возможно, ухудшится, если для этого ресурса удерживается блокировка.

Если вы можете использовать Visual Studio 2010, взгляните на библиотеку шаблонов параллельных вычислений и библиотеку асинхронных агентов, там есть несколько инструментов, которые помогут сократить объем написания кода.

Если вы не можете, вы можете, по крайней мере, попробовать разместить токен в TLS, который показывает, был ли COM инициализирован в этом потоке, и использовать наличие этого токена, чтобы обойти ваши вызовы CoInitialize, когда они не нужны.

person Rick    schedule 04.02.2010
comment
30 секунд — это определенно CoInitializeEx/CoUninitialize. Я работаю исходя из предположения, что мне не следует изменять состояние рабочего потока, управляемого пулом потоков win32, поэтому мне кажется, что необходимо вызвать CoUninitialize перед выполнением обратного вызова задачи. Работа в основном связана с дисковым вводом-выводом, но из-за других параллельных задач высокая загрузка ЦП, поэтому ЦП не остается. Спасибо за совет по VS 2010, я с нетерпением жду возможности взглянуть на него при обновлении. - person David Gladfelter; 04.02.2010

Я предполагаю, что небезопасно использовать CoInitializeEx() один раз для каждого потока и никогда не использовать CoUninitialize().

Windows выполнит очистку, если поток завершается без вызова CoUninitialize, мы знаем, что это работает, потому что в противном случае не было бы очистки при сбое или прерывании потоков.

Таким образом, единственный способ, которым этот взлом мог вызвать проблему, — это попытка кого-то поставить в очередь рабочие элементы, которым нужна квартира STA, что кажется маловероятным.

Я был бы соблазн пойти на это.

person John Knoeller    schedule 04.02.2010