Прежде всего, если мьютекс должен быть заблокирован, прежде чем делать что-либо еще, вы должны позвонить mutex_.lock()
или, по крайней мере, не игнорировать тот факт, что try_lock
может фактически не заблокировать мьютекс. Кроме того, ручное размещение вызовов для блокировки и разблокировки мьютекса чрезвычайно подвержено ошибкам и может оказаться намного сложнее, чем вы думаете. Не делай этого. Используйте вместо этого, например, std::lock_guard
.
Тот факт, что вы используете std::timed_mutex
, предполагает, что то, что на самом деле происходит в вашем реальном коде, может быть немного более сложным (зачем вам использовать std::timed_mutex
в противном случае). Предполагая, что то, что вы действительно делаете, является чем-то более сложным, чем просто вызов try_lock
и игнорирование его возвращаемого значения, рассмотрите возможность инкапсулирования вашей сложной процедуры блокировки, какой бы она ни была, в пользовательский тип защиты блокировки, например ::
class the_locking_dance
{
auto do_the_locking_dance(std::timed_mutex& mutex)
{
while (!mutex.try_lock_for(100ms))
/* do whatever it is that you wanna do */;
return std::lock_guard { mutex, std::adopt_lock_t };
}
std::lock_guard<std::timed_mutex> guard;
public:
the_locking_dance(std::timed_mutex& mutex)
: guard(do_the_locking_dance(mutex))
{
}
};
и затем создать локальную переменную
the_locking_dance guard(mutex_);
чтобы получить и удерживать замок. Это также автоматически снимет блокировку при выходе из блока.
Помимо всего этого, обратите внимание, что то, что вы здесь делаете, скорее всего, не очень хорошая идея в целом. Реальный вопрос заключается в следующем: почему существует так много разных методов, которые все должны быть защищены одним и тем же мьютексом для начала? Действительно ли вам нужно поддерживать произвольное количество потоков, о которых вы ничего не знаете, которые произвольно могут выполнять произвольные действия с одним и тем же объектом устройства в произвольные моменты времени в произвольном порядке? Если нет, то почему вы создаете Device
абстракцию для поддержки этого варианта использования? Нет ли действительно лучшего интерфейса, который вы могли бы спроектировать для своего сценария приложения, зная о том, что на самом деле должны делать потоки. Вы действительно должны сделать такую мелкозернистую блокировку? Подумайте, насколько неэффективно с вашей текущей абстракцией, например, вызывать несколько функций устройства подряд, поскольку это требует постоянной блокировки и разблокировки и блокировки и разблокировки этого мьютекса снова и снова повсюду & hellip;
С учетом всего вышесказанного может быть способ улучшить частоту блокировки, одновременно решая ваш первоначальный вопрос:
Мне интересно, существует ли конструкция C ++, шаблон проектирования или парадигма, которые можно использовать для «группировки» этих функций таким образом, чтобы вызов mutex_.try_lock()
был «неявным» для всех функций в группе.
Вы можете сгруппировать эти функции, выставив их не как методы объекта Device
напрямую, а как методы еще одного типа защиты блокировки, например
class Device
{
…
void DeviceFunction1();
void DeviceFunction2();
void DeviceFunction3();
void DeviceFunction4();
public:
class DeviceFunctionSet1
{
Device& device;
the_locking_dance guard;
public:
DeviceFunctionSet1(Device& device)
: device(device), guard(device.mutex_)
{
}
void DeviceFunction1() { device.DeviceFunction1(); }
void DeviceFunction2() { device.DeviceFunction2(); }
};
class DeviceFunctionSet2
{
Device& device;
the_locking_dance guard;
public:
DeviceFunctionSet2(Device& device)
: device(device), guard(device.mutex_)
{
}
void DeviceFunction3() { device.DeviceFunction4(); }
void DeviceFunction4() { device.DeviceFunction3(); }
};
};
Теперь, чтобы получить доступ к методам вашего устройства в пределах определенной области блока, вы сначала получаете соответствующий DeviceFunctionSet
, а затем можете вызывать методы:
{
DeviceFunctionSet1 dev(my_device);
dev.DeviceFunction1();
dev.DeviceFunction2();
}
Хорошая вещь в этом заключается в том, что блокировка происходит один раз для всей группы функций (которые, будем надеяться, будут несколько логически связаны друг с другом как группа функций, используемых для автоматического выполнения определенной задачи с вашим Device
), и вы также никогда не забуду разблокировать мьютекс & hellip;
Однако даже при этом самое важное - это не просто создать общий "потокобезопасный Device
". Эти вещи обычно ни эффективны, ни действительно полезны. Создайте абстракцию, которая отражает способ взаимодействия нескольких потоков, используя Device
в вашем конкретном приложении . Все остальное является вторым после этого. Но, ничего не зная о том, чем на самом деле является ваше приложение, на самом деле больше ничего нельзя сказать об этом & hellip;