разъяснение по использованию std :: unique_lock - PullRequest
0 голосов
/ 02 апреля 2020

Итак, у меня есть объект std :: map, доступ к которому получают несколько потоков одновременно, и я решил использовать unique_lock, чтобы сделать работу с картой безопасной.

В одной из реализаций потока объект карты используется некоторыми функциями (эти функции обычно добавляют / удаляют элементы из объекта карты), поэтому я хотел знать, будет ли определение unique_lock в верхней части родительской функции быть гарантией безопасности? или мне нужно добавить его к каждой из этих функций?

void Thread1() {
  std::unique_lock<std::mutex> ul(mutex);

  func1();  // map object is getting changed here
  func2();  // map object is getting changed here

}

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Весь смысл использования мьютекса состоит в том, чтобы предотвратить потоки B, C, D и т. Д. c. от просмотра общих данных в несогласованном состоянии из-за изменений, внесенных потоком A.

Вы предложили следующее:

void Thread1() {
    std::unique_lock<std::mutex> ul(mutex);
    func1();  // map object is getting changed here
    func2();  // map object is getting changed here
}

Отходит ли func1() карта (и, возможно, другие общие переменные) в состояние, которое не должны видеть другие темы? и func2() делает ли это снова ОК для других потоков, чтобы увидеть общие данные? Если это так, то ваш пример, вероятно, - лучшее, на что вы можете надеяться. Но если каждый из этих двух вызовов функций оставляет общие данные в «безопасном» состоянии, то вы можете рассмотреть возможность блокировки каждого из них по отдельности и разблокирования mutex.

Обычно вам лучше если потоки могут быть спроектированы так, чтобы им не часто требовался для доступа к общим данным, а когда им нужно нужен доступ к ним, они входили и выходили (т.е. блокировали и разблокировали ) как можно быстрее.


См. также, блокировка писателя читателя (на C ++, std::shared_lock) в случае, если случается, чтобы помочь с вашей конкретной проблемой.

1 голос
/ 02 апреля 2020

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

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

void Thread1() {
funcA();
{// Explicit scoping
std::unique_lock<std::mutex> ul(mutex);

func1();  // map object is getting changed here
func2();  // map object is getting changed here
}
funcB();

}

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

...