Почему многопоточность в C # не достигает 100% CPU? - PullRequest
28 голосов
/ 05 ноября 2008

Я работаю над программой, которая обрабатывает много запросов, ни один из которых не достигает более 50% процессорного времени ( в настоящее время я работаю над двухъядерным ). Поэтому я создал поток для каждого запроса, весь процесс проходит быстрее. Обрабатывая 9 запросов, один поток длится 02 мин 08 с, а при одновременной работе 3 потоков время сокращается до 01 мин 37 с, но при этом он не использует 100% ЦП, только около 50%.

Как я могу позволить моей программе использовать все возможности процессоров?

EDIT Приложение не ограничено вводом-выводом или памятью, оно постоянно находится на разумном уровне.

Я думаю, что это как-то связано с «двойным ядром».

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

Более дорогостоящая часть моего кода - это вызов dll через COM (из всех потоков вызывается один и тот же внешний метод). Эта dll также не ограничена ни памятью, ни вводом-выводом, это компонент распознавания AI, я делаю OCR-распознавание зарплаты, зарплаты для запроса.

EDIT2

Весьма вероятно, что метод STA COM является моей проблемой. Я связался с владельцами компонентов, чтобы решить эту проблему.

Ответы [ 13 ]

25 голосов
/ 05 ноября 2008

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

Кроме этого (и других ответов), очень трудно догадаться, правда. Профилировщик твой друг ...

РЕДАКТИРОВАТЬ: Хорошо, учитывая комментарии ниже, я думаю, что мы на что-то:

Более дорогая часть моего кода вызов dll через COM (тот же внешний метод вызывается из всех потоки).

Случайно ли работает метод COM в STA? Если это так, он будет использовать только один поток, сериализацию вызовов. Я сильно подозреваю, что это ключ к этому. Это похоже на блокировку вызова этого метода (правда, не совсем то же самое).

17 голосов
/ 05 ноября 2008

Проблема в объекте COM.

Большинство объектов COM выполняются в контексте «однопоточной квартиры». (Возможно, вы время от времени видели аннотацию [STAThread] для основного метода приложения .NET?)

Фактически это означает, что все отправки этому объекту обрабатываются одним потоком. Бросая больше ядер на проблему, вы получаете больше ресурсов, которые можно бездельничать, ждать или делать другие вещи в .NET.

Возможно, вы захотите взглянуть на эту статью Джо Даффи (главный специалист по .NET в Microsoft) по этой теме.

http://www.bluebytesoftware.com/blog/PermaLink,guid,8c2fed10-75b2-416b-aabc-c18ce8fe2ed4.aspx

На практике, если вам нужно сделать кучу вещей с одним COM-объектом, как этот, вы будете обмануты, потому что .NET просто сериализует шаблоны доступа внутри вашей спины. Если вы можете создать несколько COM-объектов и использовать их, вы сможете решить эту проблему, поскольку каждый из них может быть создан и доступен из отдельного потока STA. Это будет работать, пока вы не наберете около 100 потоков STA, тогда все пойдет не так. Подробнее см. Статью.

13 голосов
/ 05 ноября 2008

Вероятно, больше не процессор является узким местом для завершения вашего процесса. Узкое место, вероятно, переместилось на доступ к диску, доступ к сети или доступ к памяти. Также может возникнуть ситуация, когда ваши потоки конкурируют за блокировки.

Только вы точно знаете, что делают ваши темы, поэтому вам нужно смотреть на них с учетом вышесказанного.

4 голосов
/ 05 ноября 2008

Это зависит от того, что делает ваша программа - работа, выполняемая вашими параллельными запросами, может быть привязана к IO - ограничена скоростью (например) вашего жесткого диска - а не привязана к процессору, когда вы увидите, что ваш процессор работает на 100% ,

После редактирования звучит так, будто объекты COM STA могут быть виновником.

Все ли потоки вызывают один и тот же экземпляр COM-объекта? Можно ли было бы сделать ваш рабочий поток STA-потоками и создать отдельный экземпляр COM-объекта в каждом потоке. Таким образом можно избежать узкого места в STA.

Чтобы определить, является ли COM-класс STA:

class Test
{
  static void Main() //This will be an MTA thread by default
  {
    var o = new COMObjectClass();
    // Did a new thread pop into existence when that line was executed?
    // If so, .NET created an STA thread for it to live in.
  }
}
2 голосов
/ 11 июля 2009

Я думаю, у меня была похожая проблема. Я создавал несколько потоков в C #, которые запускали код C ++ через интерфейс COM. Мой двухъядерный процессор никогда не достигал 100%.

Прочитав этот пост, я почти сдался. Затем я попытался вызвать SetApartmentState (ApartmentState.STA) в моих потоках.

После того, как только это было изменено, максимальная загрузка процессора.

0 голосов
/ 07 ноября 2008

Проблема в объекте COM. Это STA, и у меня не может быть двух экземпляров, работающих одновременно в одном и том же процессе. Когда я создаю экземпляр для класса COM, другой становится непригодным для использования.

Я связался с разработчиками компонентов, они думают, что они могут сделать для меня.

Спасибо вам всем;)

0 голосов
/ 06 ноября 2008

Еще одно замечание. Вы пробовали запускать свой код не из Visual Studio (независимо от настроек выпуска / отладки)?

0 голосов
/ 05 ноября 2008

Возможно, я что-то неправильно понимаю, но вы сказали, что ни один из ваших запросов (каждый в отдельном потоке) не достигает 100% ЦП.

Какую операционную систему вы используете?

Кажется, я смутно припоминаю, что в старых версиях Windows (например, в ранних XP и 2000-х) загрузка ЦП учитывалась из общего числа двух процессоров, поэтому один поток не смог сделать его выше 50%, если не было простой процесс ..

0 голосов
/ 05 ноября 2008

Итак, вы решили проблему использования одного COM-объекта и теперь имеете проблему с вводом-выводом.

Увеличение времени выполнения для нескольких потоков возможно из-за смешивания случайного ввода-вывода, что замедлит все это.

Если набор данных помещается в ОЗУ, попробуйте посмотреть, сможете ли вы предварительно извлечь его в кэш. Возможно, просто чтение данных или, возможно, отображение памяти вместе с командой, чтобы сделать их доступными.

Именно поэтому базы данных SQL часто выбирают последовательное сканирование таблиц вместо сканирования индексов по запросам, которые вы не ожидаете: гораздо быстрее можно прочитать все эти данные, чем случайные куски.

0 голосов
/ 05 ноября 2008

, если ваш процесс работает на процессоре 0 и порождает там потоки, максимум, которого он когда-либо достигнет, составляет 50%. Посмотрите, есть ли у вас потоки на обоих ядрах или только на одном. Рискну предположить, что вы изолированы от одного ядра или один из ваших зависимых ресурсов заблокирован на одном ядре. Если он достигает ровно 50%, то, скорее всего, узким местом будет одно ядро.

...