я нашел это код на обмене стеками обзора кода, который реализует проблему производителя-потребителя. Я публикую раздел кода здесь.
В данном коде давайте рассмотрим сценарий, когда производитель создает значение путем вызова void add(int num)
приобретает блокировку на мьютекс mu
а также buffer.size()==size_
это заставляет производителя перейти в очередь ожидания из-за условной переменной cond
,
В это же время происходит переключение контекста и функция вызова потребителя. int remove()
потребляя значение, он пытается получить блокировку на мьютекс mu
однако, блокировка уже была получена ранее производителем, поэтому она терпит неудачу и никогда не использует значение, что вызывает тупик.
Куда я здесь не так? Поскольку код, кажется, работает правильно, когда я его запускаю, отладка не помогла мне.
Спасибо
void add(int num) {
while (true) {
std::unique_lock<std::mutex> locker(mu);
cond.wait(locker, [this](){return buffer_.size() < size_;});
buffer_.push_back(num);
locker.unlock();
cond.notify_all();
return;
}
}
int remove() {
while (true)
{
std::unique_lock<std::mutex> locker(mu);
cond.wait(locker, [this](){return buffer_.size() > 0;});
int back = buffer_.back();
buffer_.pop_back();
locker.unlock();
cond.notify_all();
return back;
}
}
Ответ OutOfBound хорош, но немного более подробно о том, что именно является «атомарным», полезно.
wait
операция над условной переменной имеет предусловие и постусловие, что переданный в мьютекс блокируется вызывающей стороной. wait
Операция разблокирует мьютекс изнутри и делает это таким образом, чтобы гарантированно не пропустить notify
или же notify_all
операции из других потоков, которые происходят в результате разблокировки мьютекса. внутри wait
разблокировка мьютекса и переход в состояние ожидания уведомлений являются атомарными по отношению друг к другу. Это позволяет избежать сна / пробуждения гонок.
Условная форма критической секции внутренне проверяет предикат. Это все еще зависит от уведомлений, сделанных правильно как бы то ни было.
В каком-то смысле можно думать о wait
как это сделать:
while (!predicate()) {
mutex.unlock();
/* sleep for a short time or spin */
mutex.lock();
}
Переменная условия с уведомлениями позволяет закомментированной строке в середине быть эффективной. Который дает:
while (!predicate()) {
atomic { /* This is the key part. */
mutex.unlock();
sleep_until_notified();
}
mutex.lock();
}
Идея для std::condition_variable::wait(lock, predicate)
Это то, что вы ждете, пока предикат не будет встречен, и впоследствии получите блокировку мьютекса. Чтобы сделать это атомарно (что важно в большинстве случаев), вы должны сначала заблокировать мьютекс, затем ожидание освободит его и заблокирует для проверки предиката. Если оно выполнено, мьютекс остается заблокированным, и выполнение продолжается. Если нет, мьютекс будет освобожден снова.