Несколько мьютексов для нескольких переменных - PullRequest
0 голосов
/ 01 февраля 2019

Исходя из предыдущего вопроса :

У меня есть несколько длинных назначений для различных переменных, которые совместно используются потоками.Имеет ли смысл разделять эти назначения в одной области, чтобы один и тот же lock_guard не занимал все переменные?

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

Я предполагаю, что это зависит от сложности вызываемой функции.Если у вас есть только назначение, это, безусловно, быстрее, чем второй lock_guard, блокирующий / разблокирующий другой мьютекс.

Псевдокод

// all used variables beside the lock_guards are created and initialized somewhere else

// do something ...

{
    std::lock_guard<std::mutex> lock(mut0);
    shared_var0 = SomeFunctionTakingSameTime0();  
}

{
    std::lock_guard<std::mutex> lock(mut1);
    shared_var1 = SomeFunctionTakingSameTime1();  
}

// do this again or other stuff ...

Какие могут быть преимущества или недостатки дляэтот тип структуры кода?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Имеет ли смысл разделять эти назначения в одной области, чтобы один и тот же lock_guard не занимал все переменные?

True.Действительно, ваша интуиция верна.

Какие могут быть преимущества или недостатки для такого типа структуры кода?

Однако такую ​​ситуацию нельзя предсказать с оченьограниченный фрагмент кода.Лучшее предложение здесь - выполнить некоторые тесты.

Некоторые важные аспекты, которые я лично учел бы:

  • тип shared_var0?
  • время блокировкимьютекс> время для завершения SomeFunctionTakingSameTime1?

Как вы сказали, если ваши функции SomeFunctionTakingSameTime0 и SomeFunctionTakingSameTime1 займут значительное количество времени, затем разделите на дваразные области видимости могут помочь максимизировать пропускную способность.

Как правило, критическая секция (т. е. область, в которой вы удерживаете блокировку) должна быть как можно короче.

Конечно, как и все, есть компромисс.Операции lock / unlock не являются бесплатными.

int shared; 
lock(mutex);
shared = 1;
unlock(mutex);

lock(mutex);
shared = 2;
unlock(mutex);

Конечно, это глупый пример.Но это показывает критический аспект в операции захвата блокировки.Операция присваивания в этом примере намного дешевле, чем двойная блокировка мьютекса!Более того, это предотвратит некоторые оптимизации компилятора (назначение shared = 1 может быть полностью удалено - с семантической точки зрения -).

Заключение, если тип shared_var0 и shared_var1 является фундаментальным(int, float и т. Д.) Вы даже можете рассмотреть возможность сохранить их в std::atomic и полностью избежать мьютексов.

0 голосов
/ 01 февраля 2019

Из вашего описания я предполагаю, что SomeFunctionTakingSameTime0() является несколько трудоемкой функцией.Но единственный способ взаимодействия с общими переменными - это присвоение.Следовательно, может быть лучше сделать что-то вроде этого:

auto temp = SomeFunctionTakingSameTime0();
{
    std::lock_guard<std::mutex> lock(mut0);
    shared_var0 = std::move(temp);  
}

Поскольку вы дали два разных мьютекса mut0 и mut1 для замков, лучшее, что вы можете сделать, - это приобрести их отдельно.

...