В какой ситуации вы используете семафор над мьютексом в C ++? - PullRequest
35 голосов
/ 28 февраля 2010

Во всех ресурсах, которые я читал о многопоточности, мьютекс чаще используется и обсуждается, чем семафор.Мой вопрос: когда вы используете семафор поверх мьютекса?Я не вижу семафоров в теме Boost.Значит ли это, что семафоры больше не используются в наши дни?

Насколько я понимаю, семафоры позволяют разделять ресурс между несколькими потоками.Это возможно, только если эти потоки только читают ресурс, но не пишут.Это правильно?

Ответы [ 9 ]

22 голосов
/ 28 февраля 2010

Типичный вариант использования мьютекса (разрешающий доступ только к одному потоку к ресурсу в любое время) гораздо более распространен, чем типичный вариант использования семафора. Но семафор на самом деле является более общей концепцией: мьютекс - это (почти) особый случай семафора.

Типичные приложения: Вы не хотите создавать более (например, 5) соединений с базой данных. Независимо от того, сколько рабочих потоков существует, они должны совместно использовать эти 5 соединений. Или, если вы работаете на N-ядерном компьютере, вы можете убедиться, что некоторые задачи, интенсивно использующие ЦП / память, не выполняются одновременно в более чем N потоках (поскольку это приведет к снижению пропускной способности только из-за переключения контекста и эффекты кеширования кеша). Возможно, вы даже захотите ограничить число параллельных задач, интенсивно использующих процессор / память, до N-1, поэтому остальная часть системы не будет голодать. Или представьте, что определенной задаче требуется много памяти, поэтому одновременное выполнение более N экземпляров этой задачи приведет к подкачке страниц. Вы можете использовать семафор, чтобы убедиться, что одновременно выполняется не более N экземпляров этой конкретной задачи.

РЕДАКТИРОВАТЬ / PS: из вашего вопроса "Это возможно, только если эти потоки только читают ресурс, но не пишут. Это правильно?" и ваш комментарий, мне кажется, что вы думаете о ресурсе как о переменной или потоке, который может быть прочитан или записан и который может быть записан только одним потоком за раз. Не. Это вводит в заблуждение в этом контексте.

Думайте о таких ресурсах, как "вода". Вы можете использовать воду для мытья посуды. Я могу использовать воду для мытья посуды одновременно. Для этого нам не нужна никакая синхронизация, потому что нам обоим достаточно воды. Мы не обязательно используем ту же воду. (И вы не можете «читать» или «писать» воду.) Но общее количество воды равно конечному . Таким образом, любое количество участников не может помыть посуду одновременно. Этот вид синхронизации выполняется с помощью семафора. Только обычно не с водой, а с другими ограниченными ресурсами, такими как память, дисковое пространство, пропускная способность ввода-вывода или ядра процессора.

11 голосов
/ 28 февраля 2010

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

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

Следовательно, мьютексы обычно используются для защиты параллелизма ресурсов (то есть: исключение MUTual), в то время как семафоры используются для передачи сигналов между потоками (например, сигнализация флагов семафоров между судами). Мутекс сам по себе не может использоваться для передачи сигналов, но семафоры могут. Таким образом, выбор одного зависит от того, что вы пытаетесь сделать.

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

9 голосов
/ 28 февраля 2010

Для управления доступом к ограниченному количеству ресурсов, совместно используемых несколькими потоками (внутри- или внутрипроцессными).

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

Итак, мы выделили N из этих ресурсов и поместили их за семафором, инициализированным для N. Когда больше чем N потоков пытались использовать ресурс, они просто блокировались, пока один из них не был доступен.

4 голосов
/ 28 февраля 2010

Boost.Thread имеет мьютексы и условные переменные. Чисто с точки зрения функциональности семафоры поэтому избыточны [*], хотя я не знаю, поэтому ли они и опущены.

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

Ресурсы чтения и записи - это IMO с красной сельдью, они не имеют никакого отношения к разнице между мьютексом и семафором. Если вы используете счетный семафор, вы можете столкнуться с ситуацией, когда несколько потоков одновременно обращаются к одному и тому же ресурсу, и в этом случае он, вероятно, должен иметь доступ только для чтения. В этой ситуации вы могли бы вместо этого использовать shared_mutex из Boost.Thread. Но семафоры «не для» защиты ресурсов, как мьютексы, они «для» отправки сигнала из одного потока в другой. Можно использовать их для управления доступом к ресурсу.

Это не означает, что все виды использования семафоров должны относиться к ресурсам только для чтения. Например, вы можете использовать двоичный семафор для защиты ресурса чтения / записи. Впрочем, это может быть плохой идеей, поскольку мьютекс часто дает вам лучшее планирование.

[*] Вот примерно как вы реализуете счетный семафор, используя мьютекс и условную переменную. Конечно, для реализации общего семафора вам нужен общий мьютекс / condvar:

struct sem {
    mutex m;
    condvar cv;
    unsigned int count;
};

sem_init(s, value)
    mutex_init(s.m);
    condvar_init(s.cv);
    count = value;

sem_wait(s)
    mutex_lock(s.m);
    while (s.count <= 0) {
        condvar_wait(s.cv, s.m);
    }
    --s.count;
    mutex_unlock(s.m);

sem_post(s)
    mutex_lock(s.m);
    ++s.count;
    condvar_broadcast(s.cv)
    mutex_unlock(s.m);

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

3 голосов
/ 28 февраля 2010

Мне кажется, что не существует простого способа ДЕЙСТВИТЕЛЬНО ответить на ваш вопрос, не игнорируя важную информацию о семафорах. Люди написали много книг о семафорах , поэтому любой ответ из одного или двух абзацев является плохой услугой. Популярная книга Маленькая книга семафоров ... для тех, кто не любит большие книги:).

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

Обновление:
Дэн указал на некоторые ошибки в моих примерах, я оставлю это со ссылками, которые предлагают НАМНОГО лучших объяснений, чем мои :).

Вот ссылки, показывающие ПРАВИЛЬНЫЕ способы использования семафора:
1. Статья IBM
2. Лекция Чикагского университета
3. Статья о Netrino, которую я первоначально разместил.
4. В «продаже билетов» бумага + код.

1 голос
/ 28 февраля 2010

Как взято из этой статьи :

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

Наконец, класс Семафор. Допустим, у вас есть метод, который действительно сильно загружает процессор, а также использует ресурсы, к которым вам нужно управлять доступом (используя Mutexes :)). Вы также определили, что максимум пять вызовов метода - это почти все, что может обработать ваша машина, не делая его безразличным. Лучшим решением здесь является использование класса Semaphore, который позволяет ограничить определенное количество потоков доступа к ресурсу.

0 голосов
/ 20 января 2015

Семафоры изначально задумывались для синхронизации между процессами. Windows использует WaitForMultipleObjects, который похож на семафор. В мире Linux начальная реализация pthread не позволяла использовать мьютекс в процессе. Теперь они делают. Концепция разрушения атомарного приращения (инкрементного приращения в Windows) вместе с легковесным мьютексом является наиболее практичной реализацией в наши дни после того, как потоки стали единицей планирования для процессора. Если инкремент и блокировка были вместе (семафор), время для получения / снятия блокировок будет слишком длинным, и мы не сможем разделить эти две функции модуля, как мы делаем это сегодня для повышения производительности и улучшения конструкций синхронизации.

0 голосов
/ 28 февраля 2010

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

Mutex сильно зависят от реализации. Они были оптимизированы для их двоичной блокировки. Обычный вариант использования мьютекса - это двоичный семафор.

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

0 голосов
/ 28 февраля 2010

Насколько я понимаю, семафоры - это термин, сильно связанный с IPC. Это по-прежнему означает защищенную переменную, которую могут изменять многие процессы, но среди процессов и эта функция поддерживается ОС.

Обычно нам не нужна переменная, а простой мьютекс покрывает все наши требования. Если нам все еще нужна переменная, возможно, мы сами ее кодируем - «переменная + мьютекс», чтобы получить больше контроля.

Резюме: мы не используем семафоры в многопоточности, потому что обычно используем мьютекс для простоты и контроля, и мы используем семафоры для IPC, потому что он поддерживается ОС и официальным именем для механизма синхронизации процессов.

...