确保,函数在 lock_guard 下运行
Assure, function runs under lock_guard
我有一个函数 foo()
,它在繁重的多线程环境中从不同的地方调用。
它正在修改一些敏感数据,但由于复杂性,我 不能 使用 inside 这个函数 std::lock_guard...
- 这必须由调用函数保证。原因是该函数被连续调用多次,修改后的数据 [inside 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 );
...
}
我的问题:
这样可以吗,或者有更好的办法吗?不能使用可重入互斥。
如果 foo
应该始终使用相同的互斥锁,例如,如果它是一个成员函数并且 class 持有一个互斥锁,我会采用这种方法:
void foo() {
std::lock_guard<std::mutex> lock(someMutex);
...
}
否则,它通常会被推送到 method/class 的调用者,以调节来自外部不同线程的调用函数到使用锁的调用,如下所示:
{
std::lock_guard<std::mutex> lock(someMutex);
foo();
}
如果您真的认为这很容易出错(我不认为),您可以将它包装在 lambda 中以方便使用:
auto lockedFoo = [&]{ std::lock_guard<std::mutex> lock(someMutex); foo(); };
我确信,我的开始方式是正确的。我发现这个 post On unnamed parameters to functions, C++
这或多或少证实了我。
现在,我去掉了参数名,它是这样实现的:
auto foo( std::lock_guard<std::mutex> & ) -> void
{
...
}
我觉得它更优雅,因为它不使用 gcc 扩展并且完全兼容 C++。
最全面的方法是创建一个'transaction object'。它是 'Delegation Pattern' 的变体,其中事务对象协调对对象的访问。可以认为是速度上的lock_guard<>
。与 lock_guard<>
一样,它使用 RAII 锁定然后解锁对象,允许在其间进行多个操作。
也像 lock_guard<>
优化可能只是解包所有内联调用并导致代码与直接管理 mutex
.
一样高效
此示例使用 private
禁止在事务外访问 'lock required' 成员。
在许多情况下,这肯定会过度设计,特别是考虑到完整的实现需要 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;
}
我有一个函数 foo()
,它在繁重的多线程环境中从不同的地方调用。
它正在修改一些敏感数据,但由于复杂性,我 不能 使用 inside 这个函数 std::lock_guard...
- 这必须由调用函数保证。原因是该函数被连续调用多次,修改后的数据 [inside 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 );
...
}
我的问题:
这样可以吗,或者有更好的办法吗?不能使用可重入互斥。
如果 foo
应该始终使用相同的互斥锁,例如,如果它是一个成员函数并且 class 持有一个互斥锁,我会采用这种方法:
void foo() {
std::lock_guard<std::mutex> lock(someMutex);
...
}
否则,它通常会被推送到 method/class 的调用者,以调节来自外部不同线程的调用函数到使用锁的调用,如下所示:
{
std::lock_guard<std::mutex> lock(someMutex);
foo();
}
如果您真的认为这很容易出错(我不认为),您可以将它包装在 lambda 中以方便使用:
auto lockedFoo = [&]{ std::lock_guard<std::mutex> lock(someMutex); foo(); };
我确信,我的开始方式是正确的。我发现这个 post On unnamed parameters to functions, C++ 这或多或少证实了我。
现在,我去掉了参数名,它是这样实现的:
auto foo( std::lock_guard<std::mutex> & ) -> void
{
...
}
我觉得它更优雅,因为它不使用 gcc 扩展并且完全兼容 C++。
最全面的方法是创建一个'transaction object'。它是 'Delegation Pattern' 的变体,其中事务对象协调对对象的访问。可以认为是速度上的lock_guard<>
。与 lock_guard<>
一样,它使用 RAII 锁定然后解锁对象,允许在其间进行多个操作。
也像 lock_guard<>
优化可能只是解包所有内联调用并导致代码与直接管理 mutex
.
此示例使用 private
禁止在事务外访问 'lock required' 成员。
在许多情况下,这肯定会过度设计,特别是考虑到完整的实现需要 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;
}