首先,如果互斥体必须在你做任何其他事情之前被锁定,那么你应该调用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 抽象来支持这个用例?真的没有更好的界面可以为您的应用程序场景设计,知道线程应该做什么。你真的需要做这种细粒度的锁定吗?考虑一下你当前的抽象是多么低效,例如,连续调用多个设备函数,因为这需要不断地锁定和解锁以及在整个地方一次又一次地锁定和解锁这个互斥体......
话虽如此,可能有一种方法可以提高锁定频率,同时解决您最初的问题:
我想知道是否有 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 完成特定任务),然后您也永远不会忘记解锁互斥锁……
不过,即便如此,最重要的是不要仅仅构建一个通用的“线程安全Device”。这些东西通常既没有效率也没有真正有用。 在您的特定应用程序中构建一个抽象,以反映多个线程应该使用Device 协作的方式。其他一切都是其次的。但是在不知道您的应用程序实际上是什么的情况下,没有什么可以说的了……