Планирование цепочек и потоков для производителей в .net - PullRequest
3 голосов
/ 21 августа 2010

Я создал цепочку из 4 потоков производитель-потребитель (образуя 4-х шаговый конвейер).К моему изумлению, все четыре потока работают последовательно, а не одновременно !!!То есть второй поток загадочным образом ожидает, пока первый поток полностью не завершит свою работу.Третий поток таинственно ожидает, пока второй поток полностью не завершит работу (и т. Д.).

Становится хуже.Если я помещаю Thread.Sleep (300) в цикл первого производителя, то остальные три потока становятся параллельными и фактически получают процессорное время, как и ожидалось, производя консольный вывод «случайным чередованием», как и ожидалось от многопоточного приложения.Я почти не могу принять идею о том, что «сон» является необходимой частью решения, и все же я вижу, что сон включается именно таким образом в код , написанный Джоном Скитом .

Скажите, пожалуйста, что для достижения параллелизма нет необходимости, или, если это так, то почему ?

Более точная история о моей конкретной цепочке производитель-потребитель выглядитthis:

  1. Первый поток: в тесном цикле генерирует сообщения «виджетов» как можно быстрее, помещая их в очередь для следующего потока.System.Threading.Timer устанавливается на ~ 100 миллисекунд, когда первый виджет добавляется в очередь.Обратный вызов, запускаемый из таймера, является вторым потоком ...
  2. Второй поток (запускается из таймера): считывает некоторые или все виджеты из предыдущей очереди.Он отправляет их в другую очередь (для использования третьим потоком).Механизм monitor.Pulse / Wait используется для синхронизации с третьим потоком.
  3. Третий поток: Блокирует монитор. Подождите, пока не будет вызван monitor.Pulse, затем выберет один элемент изочередь.Один элемент помещается в последнюю очередь, снова с помощью монитора. Импульс по окончании отправки.
  4. четвертый поток: Блокировка на мониторе. Подождите, пока не будет вызван монитор. Импульс.Виджеты обрабатываются.

Обработка 1 миллиона виджетов по этому конвейеру занимает около 4 минут.Через 4 минуты у ПЛЕНТИЙ будет достаточно времени для планирования трех последних потоков и выполнения некоторой работы одновременно с первым потоком.Но, как я уже сказал, последние три потока выполняются последовательно, если я не введу крошечный сон для первого потока.Это не имеет никакого смысла.

Есть мысли о том, почему это работает таким образом?

ps Пожалуйста, не говорите мне, что длинная цепочка производитель-потребитель, как я описал, может быть сокращена илиустранены.Пожалуйста, поверьте мне (или предположите), что мне нужна такая длинная цепь.:)

Ответы [ 2 ]

1 голос
/ 21 августа 2010

Плотные петли воздействуют на многопоточность. Похоже, ваш первый поток работает достаточно быстро, и второй поток даже не получает возможности начать. Обратите внимание , что если это произойдет, то последовательное решение является наиболее эффективным. :)

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

Хотя вы можете обойти это поведение, добавив Thread.Sleep с ненулевым аргументом (см. запись в блоге Джо Даффи о том, почему это работает ) или просто проигнорируйте его, лучшее решение - ограничить размер первой очереди производителя / потребителя. Это позволит только первому потоку генерировать определенное количество виджетов, а затем блокировать, пока остальная часть конвейера не сможет начать.

.NET 4.0 BlockingCollection<T> позволяет указать максимальный размер. Библиотека Microsoft Rx перенесла это в .NET 3.5, так что вы можете использовать это, если хотите. (Я рекомендую это вместо использования собственного решения).

0 голосов
/ 21 августа 2010

Позвольте мне угадать - вы, скорее всего, используете какой-то механизм блокировки?Я хотел бы предложить реализацию блокировок сообщений без блокировок, чтобы повысить вашу производительность.Либо это, либо вы заставляете их всех в один и тот же аппаратный поток, но я не уверен, что вы можете сделать это в Windows.

Я бы посоветовал, начав читать эту реализацию без блокировки потребителя.Очередь производителей, которая работает с 1 производителем и 1 потребителем: многопоточный один производитель-один потребитель без блокировок: http://www.codeproject.com/KB/threads/LockFree.aspx#heading0005

Я также рекомендовал бы прочитать немного о volatile: http://www.drdobbs.com/cpp/212701484

Херб Саттер написал хорошую статью, которая напоминает вам об опасности написания такого рода кода: http://www.drdobbs.com/cpp/210600279;jsessionid=ZSUN3G3VXJM0BQE1GHRSKHWATMY32JVN?pgno=2

Наконец, я бы предложил прочитать эту статью и для другой очереди без блокировки: http://www.drdobbs.com/architecture-and-design/210604448

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

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...