Правда ли, что C ++ 0x придет без семафоров? Уже есть несколько вопросов о переполнении стека относительно использования семафоров. Я использую их (семафоры posix) все время, чтобы позволить потоку ожидать какого-то события в другом потоке:
void thread0(...)
{
doSomething0();
event1.wait();
...
}
void thread1(...)
{
doSomething1();
event1.post();
...
}
Если бы я сделал это с мьютексом:
void thread0(...)
{
doSomething0();
event1.lock(); event1.unlock();
...
}
void thread1(...)
{
event1.lock();
doSomethingth1();
event1.unlock();
...
}
Проблема: Это уродливо и не гарантируется, что thread1 сначала блокирует мьютекс (учитывая, что тот же поток должен блокировать и разблокировать мьютекс, вы также не можете заблокировать event1 до запуска thread0 и thread1).
Итак, поскольку у boost тоже нет семафоров, каков самый простой способ достичь вышеуказанного?
Ответы:
Вы можете легко создать его из мьютекса и условной переменной:
источник
while(!count_)
цикла.Исходя из ответа Максима Егорушкина , я попытался сделать пример в стиле C ++ 11.
источник
cv.wait(lck, [this]() { return count > 0; });
Я решил написать самый надежный / универсальный семафор C ++ 11, какой только мог, в стиле стандарта, насколько я мог (заметьте
using semaphore = ...
, вы обычно просто используете имя,semaphore
похожее на обычное использованиеstring
notbasic_string
):источник
wait_for
иwait_until
вызовы метода предиката возвращают логическое значение ( а не `станд :: cv_status).std::size_t
без знака, поэтому уменьшение его ниже нуля равно UB, и всегда будет>= 0
. ИМХОcount
должно бытьint
.в соответствии с семафорами posix, я бы добавил
И я предпочитаю использовать механизм синхронизации на удобном уровне абстракции, а не всегда копировать, вставляя сшитую версию, используя более простые операторы.
источник
Вы также можете проверить cpp11-on-multicore - он обладает переносимой и оптимальной реализацией семафора.
Хранилище также содержит другие полезные свойства потоков, которые дополняют потоки c ++ 11.
источник
Вы можете работать с мьютексом и условными переменными. Вы получаете эксклюзивный доступ с мьютексом, проверьте, хотите ли вы продолжить или нужно ждать другого конца. Если вам нужно ждать, вы ждете в состоянии. Когда другой поток определяет, что вы можете продолжить, он сигнализирует об этом условии.
В библиотеке boost :: thread есть короткий пример, который вы, скорее всего, можете просто скопировать (библиотеки C ++ 0x и Boost очень похожи).
источник
wait()
преобразуйте в «блокировку, проверьте счетчик, если ненулевое уменьшение, и продолжите; если нулевое ожидание при условии», в то время какpost
будет «блокировка, счетчик приращений, сигнализируйте, если это было 0 "Также может быть полезна обертка семафора RAII в потоках:
Пример использования в многопоточном приложении:
источник
C ++ 20 наконец-то будет иметь семафоры -
std::counting_semaphore<max_count>
.Они будут иметь (как минимум) следующие методы:
acquire()
(Блокировка)try_acquire()
(неблокирует, немедленно возвращает)try_acquire_for()
(неблокирование, длительность)try_acquire_until()
(неблокирование, требуется время, чтобы прекратить попытки)release()
Это еще не указано в cppreference, но вы можете прочитать эти слайды презентации CppCon 2019 или посмотреть видео . Также есть официальное предложение P0514R4 , но я не уверен, что это самая актуальная версия.
источник
Я нашел, что shared_ptr и weak_ptr, длинный со списком, выполнил работу, в которой я нуждался. Моя проблема заключалась в том, что у меня было несколько клиентов, желающих взаимодействовать с внутренними данными хоста. Обычно хост обновляет данные самостоятельно, однако, если клиент запрашивает его, хост должен прекратить обновление, пока клиенты не получат доступ к данным хоста. В то же время клиент может запросить монопольный доступ, чтобы ни другие клиенты, ни хост не могли изменить эти данные хоста.
Как я это сделал, я создал структуру:
У каждого клиента будет такой член:
Тогда у хоста будет элемент weak_ptr для исключительности и список слабых_приятий для неисключительных блокировок:
Существует функция для включения блокировки и другая функция для проверки, заблокирован ли хост:
Я проверяю блокировки в LockUpdate, IsUpdateLocked и периодически в процедуре обновления хоста. Тестирование на блокировку так же просто, как проверка, истек ли срок действия weak_ptr, и удаление всех просроченных из списка m_locks (я делаю это только во время обновления хоста), я могу проверить, пуст ли список; в то же время, я получаю автоматическую разблокировку, когда клиент сбрасывает shared_ptr, за который он висит, что также происходит, когда клиент автоматически уничтожается.
Общий эффект заключается в том, что клиенты редко нуждаются в эксклюзивности (обычно зарезервированы только для добавления и удаления), большую часть времени запрос к LockUpdate (false), то есть неисключительный, выполняется до тех пор, пока (! M_exclusiveLock). И LockUpdate (true), запрос на эксклюзивность, успешно выполняется только тогда, когда оба (! M_exclusiveLock) и (m_locks.empty ()).
Можно было бы добавить очередь для смягчения исключительных и неисключительных блокировок, однако у меня до сих пор не было коллизий, поэтому я собираюсь подождать, пока это произойдет, чтобы добавить решение (в основном, поэтому у меня есть условия тестирования в реальном мире).
Пока это хорошо работает для моих нужд; Я могу представить необходимость его расширения и некоторые проблемы, которые могут возникнуть при расширенном использовании, однако, это было быстро реализовано и требовало очень мало пользовательского кода.
источник
В случае, если кто-то заинтересован в атомарной версии, вот реализация. Производительность ожидается лучше, чем версия переменной mutex & condition.
источник
wait
код должен повторяться несколько раз. Когда он, наконец, разблокируется, он возьмет на себя мать всех непредсказуемых ветвей, так как предсказание цикла ЦП, безусловно, предскажет, что оно будет повторяться снова. Я мог бы перечислить еще много проблем с этим кодом.wait
фактор снижения производительности: цикл будет потреблять ресурсы микропроцессора ЦП по мере его вращения. Предположим, что он находится в том же физическом ядре, что и предполагаемыйnotify
ему поток - он ужасно замедлит этот поток.wait
цикле для одного и того же семафора. Они оба пишут на полной скорости в одну и ту же строку кэша, что может замедлить сканирование других ядер путем насыщения межъядерных шин.