Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 64
Это также означает, что длительные операции, например захват другой блокировки (даже если известно, что это не приведет к взаимоблокировке) или ожидание завершения ввода/вывода, не следует выполнять под защитой блокировки, если только это не является абсолют пой необходимостью.
В листингах 3.6 и 3.9 мы захватывали два мьютекса для операции обмела, которая очевидно требует одновременного доступа к обоим объектам. Предположим, однако, что требуется произвести сравнение простых членов данных типа >int
. В чем разница? Копирование целых чисел — дешевая операция, поэтому вполне можно было бы скопировать данные из каждого объекта под защитой мьютекса, а затем сравнить копии. Тогда мьютекс удерживался бы минимальное время, и к тому же не пришлось бы захватывать новый мьютекс, когда один уже удерживается. В следующем листинге показам как раз такой класс >Y
и пример реализации в нем оператора сравнения на равенство.
Листинг 3.10. Поочерёдный захват мьютексов в операторе сравнения
>class Y {
>private:
> int some_detail;
> mutable std::mutex m;
> int get_detail() const {
> std::lock_guard
(1)
> return some_detail;
> }
>public:
> Y(int sd): some_detail(sd) {}
> friend bool operator==(Y const& lhs, Y const& rhs) {
> if (&lhs == &rhs)
> return true;
> int const lhs_value = lhs.get_detail(); ←
(2)
> int const rhs_value = rhs.get_detail(); ←
(3)
> return lhs_value == rhs_value; ←
(4)
> }
>};
В данном случае оператор сравнения сначала получает сравниваемые значения, вызывая функцию-член >get_detail()
(2), (3). Эта функция извлекает значение, находясь под защитой мьютекса (1). После этого оператор сравнивает полученные значения (4). Отметим, однако, что наряду с уменьшением времени удержания блокировки за счет того, что в каждый момент захвачен только один мьютекс (и, стало быть, исключена возможность взаимоблокировки), мы немного изменили семантику операции по сравнению с реализацией, в которой оба мьютекса захватываются вместе. Если оператор в листинге 3.10 возвращает >true
, то это означает лишь, что значение >lhs.some_detail
в один момент времени равно значению >rhs.some_detail
в другой момент времени. Между двумя операциями считывания значения могли измениться как угодно; например, между точками (2) и (3) программа могла обменять их местами, и тогда сравнение оказалось бы вообще бессмысленным. Таким образом, возврат оператором сравнения значения >true
, означает, что значения были равны, пусть даже ни в какой момент времени фактическое равенство не наблюдалось. Очень важно следить, чтобы такие изменения семантики операций не приводили к проблемам: