2012-02-21 76 views
2

我有一个由多个线程共享的对象,我想锁定各个成员变量,而不锁定整个对象,以便不同的线程可以访问不同的成员变量同时。 阅读了一些文章后,我使用shared_mutex和getter()/ setter()函数编写代码。C++ getter/setter,互斥锁,细粒度锁定

class Test 
    { 
    public: 
    **// variable, shared_mutex and getter/setter for x** 
    double x; 
    boost::shared_mutex x_mutex; 
    double x_getter(); 
    void x_setter(); 
    **// variable, shared_mutex and getter/setter for y** 
    ...... 
    **// variable, shared_mutex and getter/setter for z** 
    ...... 
    }; 

    double Test::x_getter() 
    { 
     // get shared access 
     boost::shared_lock lock(_access); 
     return x; 
    } 

    void Test::x_setter() 
    { 
     // get exclusive access 
     boost::unique_lock lock(_access); 
     // do something with x; 
    } 

    //getter/setter functions for y and z. 
    ...... 

该代码看起来笨拙,尤其是当成员变量的数量增加时。我想知道这类问题是否有更好的解决方案。

谢谢。

回答

7

既然你明明只需要为实际读取/写入数据的时间短的锁,你可以只用控制数据为一类,你那么作为成员变量使用它封装:

// note: you probably should add constructors as well 
template<typename T> struct synchronized 
{ 
public: 
    synchronized& operator=(T const& newval) 
    { 
    boost::unique_lock lock(mutex); 
    value = newval; 
    } 
    operator T() const 
    { 
    boost::unique_lock lock(mutex); 
    return value; 
    } 
private: 
    T value; 
    boost::shared_mutex mutex; 
}; 

class Test 
{ 
public: 
    synchronized<double> x; 
    synchronized<int> y; 
    synchronized<std::string> z; 
}; 

void foo(Test& t) 
{ 
    double read = t.x; // locked, via synchronized<double>::operator double() const 
    t.x = 3.14;  // locked, via synchronized<double>::operator= 
} 
+0

谢谢你,celtschk,我实现了你的方法,代码看起来更干净。 – 2607 2012-02-22 14:59:42

1

你是正确的,这种方法看起来很笨拙,很快就变得难以管理。因此,我尝试通过打破数据依赖性来模拟多线程问题。但是,如果没有进一步的背景知识,我不能建议如何对问题进行建模。

如果你已经投资了这样的架构,它的改变为时已晚,那么我会考虑这个。

template<class T> 
class SharedValiable 
{ 
    private: 
     T     myT; 
     boost::shared_mutex myTMutex; 

    public: 
     // 
     // Implement appropriate copy, assign and default 
     // to ensure proper value semantics 
     // 

     T getter() const 
     { 
      boost::shared_lock lock(_access); 
      return x; 
     } 

     void setter() 
     { 
      boost::unique_lock lock(_access); 
     } 
} 

这允许每个变量按照您的初始意图进行保护,但可以更轻松地从类中添加新的或删除的成员。此外,该模板可以专用于某些可以使用原子操作系统操作(如int)的类型。例如:

template<int> 
class SharedValiable 
{ 
    private: 
     T     myT; 

    public: 
     // 
     // Implement appropriate copy, assign and default 
     // to ensure proper value semantics 
     // 

     T getter() const 
     { 
      // no need to lock, updates are atomic 
      return x; 
     } 

     void setter() 
     { 
      // no mutex needed we will use an atomic OS op to update 
      InterlockedCompareAndExchange(myT, newVal); 
     } 
} 
0

总的来说,我会建议不要这样做。通常,与班级一起努力的目的是在进入和离开某个方法时确保某种对象状态的某种保证。例如,在一个列表对象中,你需要链接处于一致状态,你需要计数是正确的,等等。只是锁定个体变量很少允许发生。如果你在没有锁定计数状态的情况下改变一个链接,当你在更新过程中被另一个线程查询时,你就会说出一个或另一个。更糟糕的是,您可以同时更换两个链接,并最终得到一张不再适用的扭曲列表,这取决于您的存储空间。当然这是可能的一个例子,但你应该明白。

如果你想让多个线程能够查看你的对象的状态,那么你需要一个读写器锁定整个对象,这将允许尽可能多的读者,但不允许他们看到对象中期更新。

+0

尽管我普遍认同,但有理由拥有公开的数据,这本身并不是线程安全的,而且更适合作为评论。 – 2012-02-21 21:46:48

+0

对于顶部的谩骂,你可能是正确的。答案中的真正答案是使用读写器锁。 – Joel 2012-02-21 21:54:57