我有一个现有的类,其中有互斥锁的成员看起来像这样(类MembersNeedThreadSafe)...
// Just fake a mutex guard interface. Obviously doesn't do anything
class Mutex
{
public:
struct Guard
{
Guard(Mutex & M) { };
};
};
// This is the class I want to redesign.
class MembersNeedThreadSafe {
Mutex M;
int i;
double k;
// And a dozen more variables
public:
void SetI (int foo) { Mutex::Guard G(M); i = foo; }
int GetI (void) { Mutex::Guard G(M); return i; }
void SetK (double foo) { Mutex::Guard G(M); k = foo; }
double GetK (void) { Mutex::Guard G(M); return k; }
// And two dozen more methods
};
int main (void) {
MembersNeedThreadSafe bar;
bar.SetI(5);
bar.SetK(6.0);
double d = bar.GetK();
return 0;
}
我想将类MembersNeedThreadSafe重构为更像这样的设计,但它没有编译,抱怨无效使用非静态数据成员。
template <typename T, Mutex & M> class LockedVar {
typedef LockedVar<T, M> my_type;
T value;
public:
void Set(T const & foo) { Mutex::Guard G(M); value = foo; }
T const & Get (void) { Mutex::Guard G(M); return value; }
};
// I want the class to look like this...
class MembersNeedThreadSafe {
Mutex M;
public:
LockedVar <int, M> i;
LockedVar <double, M> k;
// And a dozen more variables
};
// This allows the code to run.
int main (void) {
MembersNeedThreadSafe bar;
bar.i.Set(5);
bar.k.Set(6.0);
double d = bar.k.Get();
return 0;
}
那么....我怎样才能重构第一个代码块的MembersNeedThreadSafe
类,这样我就不必为每个成员编写冗余的getter和setter方法?
附录: 我知道我可以使用这样的设计......
template <typename T> class LockedVar {
typedef LockedVar<T> my_type;
T value;
Mutex & M;
public:
LockedVar (Mutex & foo) : M(foo) { }
void Set(T const & foo) { Mutex::Guard G(M); value = foo; }
T const & Get (void) { Mutex::Guard G(M); return value; }
};
但是当我这样做时,sizeof(int)== 4,而sizeof(LockedVar)= = 16在我的编译器(gcc 4.8.2)中,它为我抛出一种红旗。看起来我应该能够通过使用互斥量作为模板参数来解决这个问题,并且如果可能的话我想知道如何这样做。
答案 0 :(得分:3)
另一种选择是稍微修改LockedVar
:
template <typename T> class LockedVar {
typedef LockedVar<T> my_type;
T value;
public:
void Set(T const & foo, Mutex & M ) { Mutex::Guard G(M); value = foo; }
T const & Get (Mutex & M) { Mutex::Guard G(M); return value; }
};
另外,如果你反复重复,虽然看起来不太好看,但你可以创建一个宏:
#define IMPL_SET_GET( t, x ) \
t x; \
void Set##x (int foo) { Mutex::Guard G(M); x = foo; } \
int Get##x (void) { Mutex::Guard G(M); return x; }
// This is the class I want to redesign.
class MembersNeedThreadSafe {
Mutex M;
// And a dozen more variables
public:
IMPL_SET_GET( int, i );
IMPL_SET_GET( int, k );
// And two dozen more methods
};
答案 1 :(得分:2)
首先,您的设计存在问题,锁的粒度允许进行以下交互:
// Thread 1 // Thread 2
if (x.GetI() != 0) {
x.SetI(0);
return y / x.GetI();
}
一般来说,您更愿意:
一种简单的方法:
class Data {
public:
friend class Behavior;
Data(): i(3), k(7) {}
private:
std::mutex mutex;
int i;
int k;
}; // class Data
class Behavior {
public:
explicit Behavior(Data& data): ref(data), guard(ref.mutex) {}
int GetI() const { return ref.i; }
void SetI(int i) { ref.i = i; }
int GetK() const { return ref.k; }
void SetK(int k) { ref.k = k; }
private:
Data& ref;
std::unique_lock<std::mutex> guard;
}; // class Behavior
请注意,互斥锁只锁定一次(在Behavior
的构造函数中),并且您不必在每个getter和setter处重复它。