Различия между условными переменными, мьютексами и замками
Например, интерфейсы C++0x
Мне трудно понять, когда использовать какие из этих вещей (cv, mutex и lock). Кто-нибудь может объяснить или указать ресурс?
Заранее спасибо.
2 ответа
На странице, на которую вы ссылаетесь, "мьютекс" - это фактический низкоуровневый синхронизирующий примитив. Вы можете взять мьютекс и затем освободить его, и только один поток может взять его в любой момент времени (следовательно, это синхронизирующий примитив). Рекурсивный мьютекс - это тот, который может быть взят одним и тем же потоком несколько раз, а затем он должен быть освобожден столько раз одним и тем же потоком, прежде чем другие смогут его использовать.
"Блокировка" - это просто класс-оболочка C++, который принимает мьютекс в своем конструкторе и освобождает его в деструкторе. Это полезно для установления синхронизации для областей C++.
Переменная условия - это более продвинутая / высокоуровневая форма синхронизирующего примитива, которая сочетает в себе блокировку с механизмом "сигнализации". Он используется, когда потокам нужно ждать, пока ресурс станет доступным. Поток может "ждать" в CV, а затем производитель ресурсов может "сигнализировать" о переменной, и в этом случае потоки, которые ожидают CV, получают уведомление и могут продолжить выполнение. Мьютекс объединяется с CV, чтобы избежать состояния гонки, когда поток начинает ожидать CV, в то время как другой поток хочет сигнализировать об этом; тогда не контролируется, доставлен или потерян сигнал.
На этот вопрос дан ответ. Я просто добавляю то, что может помочь решить, КОГДА использовать эти примитивы синхронизации.
Просто мьютекс используется для обеспечения взаимного доступа к общему ресурсу в критическом разделе нескольких потоков. Удача - это общий термин, но двоичный мьютекс можно использовать как блокировку. В современном C++ мы используем lock_guard и подобные объекты для использования RAII, чтобы упростить и обезопасить использование мьютексов. Условная переменная - это еще один примитив, который часто сочетается с мьютексом, чтобы сделать что-то известным как монитор.
Мне трудно понять, когда использовать какие из этих вещей (cv, mutex и lock). Кто-нибудь может объяснить или указать на ресурс?
Используйте мьютекс, чтобы гарантировать взаимный эксклюзивный доступ к чему-либо. Это решение по умолчанию для широкого спектра проблем параллелизма. Используйте lock_guard, если у вас есть область видимости в C++, которую вы хотите защитить с помощью мьютекса. Мьютекс обрабатывается lock_guard. Вы просто создаете lock_guard в области видимости и инициализируете его мьютексом, а все остальное C++ сделает за вас. Мьютекс освобождается, когда область видимости удаляется из стека по любой причине, включая создание исключения или возврат из функции. Это идея RAII, а lock_guard - еще один обработчик ресурсов.
Есть некоторые проблемы параллелизма, которые нелегко решить, используя только мьютекс, или простое решение может привести к сложности или неэффективности. Например, проблема производства и потребителя - одна из них. Если мы хотим реализовать потребительский поток, читающий элементы из буфера, совместно используемого с производителем, мы должны защитить буфер мьютексом, но без использования условной переменной мы должны заблокировать мьютекс, проверить буфер и прочитать элемент, если он не пуст., разблокируйте его и подождите некоторое время, снова заблокируйте и продолжайте. Это пустая трата времени, если буфер часто пуст (занят ожиданием), а также будет много блокировок, разблокировок и засыпания.
Решение проблемы производителя-потребителя должно быть более простым и эффективным. Здесь нам помогает монитор (мьютекс + условная переменная). Нам все еще нужен мьютекс, чтобы гарантировать взаимный эксклюзивный доступ, но условная переменная позволяет нам спать и ждать определенного условия. Условие здесь - производитель добавляет элемент в буфер. Производящий поток уведомляет потребительский поток о том, что в буфере есть элемент, и потребитель просыпается и получает этот элемент. Просто производитель блокирует мьютекс, помещает что-то в буфер, уведомляет потребителя. Потребитель блокирует мьютекс, засыпает, ожидая выполнения условия, просыпается, когда что-то есть в буфере, и получает элемент из буфера. Это более простое и эффективное решение.
В следующий раз, когда вы столкнетесь с проблемой параллелизма, подумайте так: если вам нужен взаимоисключающий доступ к чему-либо, используйте мьютекс. Используйте lock_guard, если хотите быть безопаснее и проще. Если проблема связана с ожиданием условия, которое должно произойти в другом потоке, вам МОЖЕТ потребоваться условная переменная.
Как правило, сначала проанализируйте свою проблему и попытайтесь найти известную проблему параллелизма, аналогичную вашей (например, см. Раздел классических проблем синхронизации на этой странице). Прочтите о решениях, предлагаемых для известного решения, чтобы выбрать лучшее. Вам может потребоваться некоторая настройка.
Я не слишком знаком с W / C++0x, так что возьмите этот ответ с частичкой соли.
Re: Mutex против замков: из документации, которую вы разместили, это выглядит как mutex
является объектом, представляющим мьютекс ОС, тогда как lock
является объектом, который содержит мьютекс, чтобы облегчить шаблон RAII.
Переменные условия - это удобный механизм, позволяющий связать механизм блокировки / сигнализации (сигнал + ожидание) с механизмом взаимного исключения, но при этом держать их в ОС изолированными, чтобы вы, как системный программист, могли выбрать связь между condvar и mutex. (полезно для работы с множеством наборов одновременно доступных объектов) Роб Кртен имеет несколько хороших объяснений condvars в одной из онлайн-глав своей книги о QNX.
Что касается общих ссылок: эта книга (еще не вышла) выглядит интересной.