Почему мьютексы отличаются от атомарных операций тем, что первый - это уровень ОС, а второй - уровень процессора? - PullRequest
0 голосов
/ 23 июня 2018

Я читаю книгу С ++ Параллелизм в действии и на странице 236-237 написано

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

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

Первый ответ на вопрос " Что является более эффективным, базовая блокировка мьютекса или атомное целое число? " говорит " атомарное блокирует шину памяти на большинстве платформ ... Невозможно приостановить поток во время блокировки шины памяти, но можно приостановить поток во время блокировки мьютекса". Учитывая, что мьютексы реализуются атомами, значит, они по сути одинаковы, верно? Я думаю, я мог бы что-то здесь упустить. Может ли кто-нибудь помочь мне с этим?

Ответы [ 2 ]

0 голосов
/ 24 июня 2018

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

Не у всех процессоров есть один из них, как у семейства 68000, у семейства PowerPC нет (думаю - исправления приветствуются. Я знаю, что это было правильным неудобством в системах PowerPC VME, где, как и машины предыдущего поколения на базе 68000, могли тестирование и установка на удаленных платах), уверен, что более ранние X86 тоже этого не сделали. Я уверен, что все основные современные процессоры это делают - это очень полезно.

Эффективно Test-and-Set дает вам счетный семафор, и именно для этого они и используются. Однако, имея в библиотеке лишь немного chicanery, он также может использоваться как мьютекс (который представляет собой двоичный семафор, который может быть передан только тем потоком, который его принял).

Семафоры и мьютексы AFAIK внедряются в наши дни с использованием операционных кодов Test-and-Set, доступных на процессорах. Однако на платформах, где нет операционного кода Test-and-Set, его поведение должно быть синтезировано ОС, возможно, с использованием ISR, отключением прерываний и так далее. Конечный результат ведет себя так же, но он значительно медленнее. Также на этих платформах «атомарный» должен быть синтезирован с использованием мьютексов для защиты значения.

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

Стоит также помнить, что вызовы для получения / передачи мьютексов включают в себя принятие решениями по планированию ядра, даже если ОС затем использует операционный код test-and-set CPU для реализации части взаимного исключения мьютекса. Принимая во внимание, что вызывать тестовый и установленный код операции непосредственно из вашей программы нет; ядро понятия не имеет, что это даже случилось. Таким образом, мьютекс - это хороший способ гарантировать, что потоки с высоким приоритетом запускаются первыми, если есть конфликт, в то время как операционный код «тестируй и устанавливай», скорее всего, нет (он будет первым пришел, первым обслужен). Это будет связано с тем, что ЦП не имеет концепции приоритета потоков, это абстрактная концепция, придуманная разработчиками ОС.

Вы можете многое узнать о том, как это делается, заглянув внутрь исходного кода библиотеки Boost C ++. Такие вещи, как общие указатели, зависят от взаимного исключения, и Boost может реализовать взаимное исключение несколькими различными способами. Например, используя оп-коды в стиле test-and-set на платформах, где они есть, или используя вызовы функций библиотеки мьютекса POSIX, или если вы скажете, что в вашей программе только 1 поток, это вообще не будет беспокоить ,

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

С помощью Boost вы можете переопределить выбор по умолчанию, используя несколько #defines. Таким образом, вы можете ускорить однопотоковую программу, скомпилировав ее без взаимного исключения в общих указателях. Это иногда действительно полезно. Чего я не знаю, так это того, потеряно ли это в C ++ 11 и более поздних версиях, теперь, когда они освоили умные указатели и сделали их своими собственными.

EDIT

Также стоит взглянуть на futexes , которые Linux использует в качестве основы для мьютексов, семафоров и т. Д. Идея futex заключается в использовании атомарных операций для реализации основной части функциональности.полностью в пространстве пользователя, прибегая к системным вызовам, только когда это абсолютно необходимо.В результате, до тех пор, пока не слишком много споров, высокоуровневые вещи, такие как мьютекс или семафор, намного эффективнее, чем в старые добрые времена, когда они всегда приводили к системному вызову.FUTEX существуют в Linux примерно с 2003 года, поэтому мы пользуемся ими уже 15 лет.По сути, нет смысла слишком беспокоиться об эффективности мьютексов по сравнению с атомарными операциями - они не слишком далеки от того, чтобы быть тем же самым.

Что, вероятно, более важно - стремиться к чистому, опрятному исходному коду, который легко читать, и использовать вызовы библиотеки, которые помогут с этим.Использование, скажем, атомарных операций над мьютексами за счет простого исходного кода, скорее всего, не стоит.Конечно, на таких платформах, как VxWorks, которые на самом деле не имеют концепции ядра / пользовательского пространства и в первую очередь рассчитаны на быстрое переключение контекста, можно позволить себе расточительность с использованием мьютексов и семафоров для достижения простоты.,

Например, использование мьютекса для управления тем, какой поток имел доступ к определенному сетевому сокету, - это способ использования приоритетов ядра и потока для управления приоритетами различных типов сообщений, отправляемых через этот сокет.Исходный код очень прост - потоки просто принимают / передают мьютекс, используя сокет, и это все, что есть.Нет администратора очередей, нет кода принятия решений по расстановке приоритетов, ничего.Все это выполняется потоками планирования ОС в ответ на мьютекс.На VxWorks это оказывается довольно эффективным, выигрывает от ОС, решающей инверсии приоритетов, и занимает очень мало времени на разработку.В Linux, особенно с установленным набором патчей PREEMPT_RT, работающим как потоки приоритетов в реальном времени, это тоже не так уж плохо (потому что это также разрешает инверсии приоритетов, что, как я понимаю, Линусу не особо важно).В то время как в ОС, в которой нет FUTEX-ов, поддерживающих мьютексы, а также имеет дорогостоящее время переключения контекста (например, Windows), это было бы неэффективно.

0 голосов
/ 23 июня 2018

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

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

...