我有一个函数foo()
,它可以在繁重的多线程环境中从不同的地方调用。
它正在修改一些敏感数据,但由于复杂性,我无法使用内部此函数std::lock_guard...
- 这必须由调用函数确保。原因是该函数连续多次被调用,并且修改后的数据[内部foo()
]必须不同时被其他线程触及,直到一个线程完成所有修改几次调用foo()
。
我们都是人 - 尤其是我 - 我正在考虑如何确保foo()
函数确实在lock_guard...
控件之下并且我并没有忘记锁定范围。
对我来说 - 在gcc下运行并且我不必考虑任何独立于平台的解决方案 - 我发现了这种方式:
auto foo( __attribute__ ((unused)) std::lock_guard<std::mutex> & guardActive ) -> void
{
...
}
然后,必须像这样调用foo()
,否则gcc不会编译它:
{
std::lock_guard<std::mutex> lockScope( mutexObject );
// block of commands calling foo() which must NOT be interrupted
...
foo( lockScope );
...
foo( lockScope );
...
foo( lockScope );
...
}
我的问题:
这样可行,还是有更好的解决方案?不能使用可重入的互斥锁。
答案 0 :(得分:0)
我确信,我开始的方式是正确的。我发现这篇帖子On unnamed parameters to functions, C++ 或多或少证实了我。
现在,我删除了参数名称,它实现如下:
auto foo( std::lock_guard<std::mutex> & ) -> void
{
...
}
我发现它更优雅,因为它不使用gcc扩展,并且它是完全C ++兼容的。
答案 1 :(得分:0)
最全面的方法是创建“交易对象”。它是“委托模式”的变体,其中事务对象协调对对象的访问。它可以被认为是速度lock_guard<>
。与lock_guard<>
类似,它使用RAII锁定然后解锁对象,允许在其间进行多个操作。
与lock_guard<>
优化一样,优化可能只需解开所有内联调用,并使代码与直接管理mutex
完全一致。
此示例使用private
禁止访问事务外的“需要锁定”成员。
在许多情况下,这肯定会过度工程,特别是考虑到完整实施需要FooConstTrans
和FooTrans
类,具体取决于引用对象的const
限定。
#include <iostream>
#include <mutex>
#include <type_traits>
class FooTrans;
class Foo {
public:
Foo():m_x(0)
{}
friend class FooTrans;
private:
int get_x() const{
return m_x;
}
void set_x(int x){
m_x=x;
}
void lock() const{
m_mutex.lock();
}
void unlock() const{
m_mutex.unlock();
}
private:
mutable std::mutex m_mutex;
int m_x;
};
class FooTrans final {
public:
FooTrans(Foo& foo):m_foo(foo){
m_foo.lock();
}
void set_x(int x){
m_foo.set_x(x);
}
int get_x(){
return m_foo.get_x();
}
~FooTrans(){
m_foo.unlock();
}
private:
Foo& m_foo;
};
int main() {
Foo foo;
FooTrans ft(foo);
ft.set_x(3);
//ft is guaranteeing the next line outputs '3' even if other threads are trying to access foo.
std::cout << ft.get_x() <<std::endl;
return 0;
}
答案 2 :(得分:-1)
如果foo
应该总是使用相同的互斥锁,例如,如果它是一个成员函数并且该类包含一个互斥锁,我会使用这种方法:
void foo() {
std::lock_guard<std::mutex> lock(someMutex);
...
}
否则,它经常被推送到方法/类的调用者,以使用如下锁定来调用来自外部不同线程的调用函数:
{
std::lock_guard<std::mutex> lock(someMutex);
foo();
}
如果你真的认为这容易出错(我不这样做)你可以将它包装在lambda中以方便:
auto lockedFoo = [&]{ std::lock_guard<std::mutex> lock(someMutex); foo(); };