Два std :: unique_lock, используемые на одном мьютексе, вызывают тупик?

я нашел это код на обмене стеками обзора кода, который реализует проблему производителя-потребителя. Я публикую раздел кода здесь.

В данном коде давайте рассмотрим сценарий, когда производитель создает значение путем вызова 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;
}
}

2

Решение

Ответ 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();
}
3

Другие решения

Идея для std::condition_variable::wait(lock, predicate)Это то, что вы ждете, пока предикат не будет встречен, и впоследствии получите блокировку мьютекса. Чтобы сделать это атомарно (что важно в большинстве случаев), вы должны сначала заблокировать мьютекс, затем ожидание освободит его и заблокирует для проверки предиката. Если оно выполнено, мьютекс остается заблокированным, и выполнение продолжается. Если нет, мьютекс будет освобожден снова.

3

По вопросам рекламы [email protected]