C ++ 11 — Почему параллелизм C ++ в действии листинг_6.1 не использует std :: recursive_mutex

Я читаю книгу «Параллелизм C ++ в действии» и у меня есть вопрос о мьютексе, использованном в листинге 6.1, фрагмент кода приведен ниже:

void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=std::move(data.top());
data.pop();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}

pop метод блокирует мьютекс и затем вызывает пустой мьютекс. Но мьютекс не является recursive_mutex, и код работает правильно. Поэтому я сомневаюсь, что на самом деле разница между std::mutex а также std::recursive_mutex,

1

Решение

Это зовет data.empty() которая выглядит как функция от члена данных. Не такой как empty Функция, которую вы показываете.

Если бы это было, это был бы рекурсивный вызов

bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}

и ничего не будет работать.

4

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

Что ж, recursive_mutex для … рекурсивной функции!

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

посмотрите на это (глупый пример)

void recursivePusher(int x){
if (x>10){
return;
}

std::lock_guard<std::mutex> lock(m);
queue.push(x);
recursivePusher(x+1);

}

эта функция рекурсивно увеличивает x и толкает его в какой-то общий queue,
как мы говорили выше — одна и та же блокировка не может быть дважды заблокирована одним и тем же потоком, но мы должны убедиться, что общая очередь не изменяется из-за нескольких потоков.

Одно простое решение — переместить локейк за пределы рекурсивной функции, но что произойдет, если мы не сможем этого сделать? что произойдет, если вызываемая функция является единственной, которая может заблокировать общий ресурс?

например, моя вызывающая функция может выглядеть так:

switch(option){

case case1: recursivly_manipulate_shared_array(); break;
case case2: recursivly_manipulate_shared_queue(); break;
case case3: recursivly_manipulate_shared_map(); break;

}

Конечно, вы не заблокируете все три (shred_Array, shared_map, shared_queue), только один из них будет изменен.

решение заключается в использовании std::shared_mutex :

void recursivePusher(int x){
if (x>10){
return;
}

std::lock_guard<std::recursive_mutex> lock(m);
queue.push(x);
recursivePusher(x+1);

}

если один и тот же поток не нужно рекурсивно блокировать мьютекс, он должен использовать обычный std::mutex, как в вашем примере.

PS. в вашем фрагменте, empty это не то же самое, что T::empty,
призвание data.empty() не звонит empty recursivley.

3

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