Зачем ставить std :: lock перед std :: lock_guard - PullRequest
0 голосов
/ 26 апреля 2018

Движение вперед с Параллелизм в действии Я достиг следующего примера.
Автор утверждает, что если мы каждый раз блокируем 2 мьютексы в одном и том же порядке, то мы гарантированно избежим взаимоблокировок .
Рассмотрим этот пример из книги:

class X
{
    private:
    some_big_object some_detail;
    std::mutex m;
public:
    X(some_big_object const& sd):some_detail(sd){}
    friend void swap(X& lhs, X& rhs)
    {
       if(&lhs==&rhs){return;}
       std::lock(lhs.m,rhs.m);
       std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
       std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
       swap(lhs.some_detail,rhs.some_detail);
    }
};
  1. Почему мы применяем std::lock, а затем применяем 2 std::lock_guards с std::adopt_lock вместо простого применения 2 std::lock_guards один после другой??
  2. Почему мы не можем просто поместить эти 2 std::mutex es в std::scoped_lock ??

Ответы [ 3 ]

0 голосов
/ 26 апреля 2018
  1. Причина в том, что std::lock блокирует мьютексы в каком-то неопределенном порядке, но порядок одинаков во всех потоках, что защищает нас от тупиков. Таким образом, это может быть lock(lhs.m), а затем lock(rhs.m) или наоборот. Это означает, что мы не знаем, какой из std::lock_guard s создать первым: для lhs.m или для rhs.m.

  2. Кажется, что книга была написана на C ++ 11 в качестве базового стандарта. std::scoped_lock поставляется только в C ++ 17.

0 голосов
/ 26 апреля 2018

Почему мы применяем std :: lock и затем применяем 2 std :: lock_guards с std :: accept_lock вместо того, чтобы просто применять 2 std :: lock_guards один за другим ??

Если вы использовали два std::lock_guard без std::lock, порядок блокировки для a = b; будет противоположен b = a;, где a и b являются X с. Если один поток попытается a = b;, а другой попытается b = a;, они могут зайти в тупик. Первый поток будет владеть блокировкой мьютекса a и ждать b, в то время как второй поток будет владеть блокировкой мьютекса b и ждать a. Использование std::lock гарантирует, что порядок блокировки всегда согласован.

Почему мы не можем просто поместить эти 2 std :: mutex в std :: scoped_lock ??

Если вы посмотрите на дату публикации статьи, на которую вы ссылались, c ++ 17 еще не существовало. Так как std::scoped_lock был представлен c ++ 17, он не мог быть использован в статье. Этот тип проблемы блокировки - это то, что std::scoped_lock предназначено для решения и должно использоваться в современном коде.

0 голосов
/ 26 апреля 2018

std::lock не RAII. Замки мьютекса не в RAII опасны и страшны. Если выдается исключение, вы можете «утечь» блокировку.

std::lock_guard не поддерживает безопасную блокировку нескольких мьютексов. Но это RAII, поэтому он делает остальную часть кода более безопасной. Если вы заблокируете a затем b в одном месте, а b затем a в другом, вы получите код, который может завести блокировку (один поток удерживает a и ожидает b, а другой поток удерживает b и ожидает a).

std::lock гарантированно избежит этого каким-либо неуказанным способом (который может включать глобальный порядок блокировок).

std::scoped_lock - это . В это то, что вы должны использовать вместо примера кода, который вы показали. Это было добавлено, потому что написание этого кода отстой. Из-за проблем, связанных с искажением имен и связыванием, было просто невозможно добавить поддержку variardic в существующие блокирующие примитивы, такие как lock guard, поэтому у него другое имя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...