В какой ситуации вы используете семафор над мьютексом в С++?

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

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

Ответ 1

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

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

Read vs. write resources - это красная селедка 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);

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

Ответ 2

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

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

EDIT/PS: Из вашего вопроса "Это возможно только в том случае, если эти потоки только читают ресурс, но не пишут. Правильно ли это?" и ваш комментарий, мне кажется, что вы думаете о ресурсе как переменной или потоке, который может быть прочитан или написан и который может быть написан только одним потоком за раз. Не. Это вводит в заблуждение в этом контексте.

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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