Преимущества использования переменных условий над мьютексом

Мне было интересно, каково преимущество в производительности от использования условных переменных по сравнению с мьютекс-блокировками в pthreads.

Я нашел следующее: "Без условных переменных у программиста должны быть постоянные опросы потоков (возможно, в критической секции), чтобы проверить, выполняется ли условие. Это может потребовать очень много ресурсов, так как поток будет постоянно занят этим. деятельность. Переменная условия - это способ достижения той же цели без опроса. " (https://computing.llnl.gov/tutorials/pthreads)

Но также кажется, что вызовы мьютекса блокируются (в отличие от спин-блокировок). Следовательно, если поток (T1) не может получить блокировку, потому что какой-то другой поток (T2) имеет блокировку, T1 переводится в спящий режим ОС и просыпается только тогда, когда T2 снимает блокировку, а ОС предоставляет T1 блокировку. Поток T1 на самом деле не опрашивает, чтобы получить блокировку. Из этого описания кажется, что использование переменных условий не дает выигрыша в производительности. В любом случае опрос не проводится. В любом случае ОС предоставляет преимущество, которое может дать парадигма условной переменной.

Можете ли вы объяснить, что на самом деле происходит.

Ответы

Ответ 1

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

Сам по себе мьютекс не делает этого.

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

Например, если у вас есть очередь элементов для работы, у вас будет мьютекс, чтобы обеспечить внутреннюю целостность очереди при доступе к различным потокам производителей и потребителей. Однако, когда очередь пуста, как узнает потребительский поток, когда что-то там будет для этого? Без чего-то вроде переменной условия ему нужно было бы опросить очередь, взяв и отпустив мьютекс в каждом опросе (иначе поток производителя никогда не мог бы положить что-то в очередь).

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

Ответ 2

Вы ищете слишком много совпадений в двух разных, но связанных вещах: мьютекс и переменная условия.

Обычный подход к реализации мьютекса заключается в использовании флага и очереди. Флаг указывает, удерживается ли мьютекс кем-либо (семафор с единым счетом тоже будет работать), и очередь отслеживает, какие потоки находятся в очереди, ожидая получения исключительно мьютекса.

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

  • Те, кто хочет приобрести мьютекс исключительно
  • Те, кто ждет, пока будет передана переменная условия

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

В игру вступает много сложных деталей, но этот набросок должен дать вам представление о структурах и операциях в игре.

Ответ 3

Если вы ищете производительность, начните чтение о алгоритмах синхронизации потоков без блокировки/не блокировки. Они основаны на атомных операциях, которые gcc достаточно любезны для обеспечения. Поиск gcc атомных операций. Наши тесты показали, что мы могли бы увеличить глобальное значение несколькими потоками, используя атомные операционные величины быстрее, чем блокирование с помощью мьютекса. Вот пример кода, который показывает, как добавлять элементы в и из связанного списка из нескольких потоков одновременно без блокировки.

Для спальных и бодрствующих потоков сигналы намного быстрее, чем условия. Вы используете pthread_kill для отправки сигнала, а sigwait - для спящего потока. Мы тестировали это тоже с такими же преимуществами производительности. Вот пример кода.