多线程环境中Singleton资源的互斥保护

时间:2010-07-22 15:05:21

标签: c++ multithreading mutex

我有一台服务器在端口上侦听请求。请求进入时,会将其分派给单个类Singleton。此Singleton类具有数据结构RootData

class Singleton {
    void process();
    void refresh();

private: RootData mRootData; }

在类中有两个函数:process:使用mRootData进行一些处理和refresh:从另一个线程定期调用,用数据库中的最新更改刷新mRootData。

要求Mutex对mRootData的访问权限进行调整。

我有以下问题:

1]如果类是单例并且mRootData在该类中,那么Mutex gaurd真的有必要吗?    我知道刷新/处理之间存在冲突是必要的。但是从服务器来看,我认为在任何给定的时间只会有一个调用过程发生(因为该类是Singleton)如果我的理解是错误的,请纠正我。

2]我应该保护i)数据结构 OR ii)访问数据结构的功能。例如。    

    i)  const RootData& GetRootData()
        {
               ACE_Read_Guard guard(m_oMutexReadWriteLock);
               return mRootData;
               // Mutex is released when this function returns
        }
        // Similarly Write lock for SetRootData()
     

    ii) void process()
        {
              ACE_Read_Guard guard(m_oMutexReadWriteLock);
              // use mRootData and do processing
              // GetRootData() and SetRootData() wont be mutex protected.
              // Mutex is released when this function returns
        }
 

3]如果上述答案是i)我应该通过引用还是按对象返回?    请在两种情况下解释。

提前致谢。

2 个答案:

答案 0 :(得分:1)

  

1]如果类是单例并且mRootData在该类中,那么Mutex gaurd真的有必要吗?

是的,因为一个主题可能会调用process()而另一个主持人正在调用refresh()

  

2]我应该保护i)数据结构还是ii)访问数据结构的功能。

Mutex旨在保护公共代码路径,即访问共享数据的函数(的一部分)。当锁定和释放发生在同一代码块中时,它最容易使用。将它们置于不同的方法几乎是一个公开的死锁邀请,因为由调用者来确保每个锁都被正确释放。

更新:如果GetRootDataSetRootData也是公共功能,那么用当前形式的互斥锁来保护它们并不重要。问题是,您正在发布对共享数据的引用,之后完全无法控制调用者可以使用它的时间和时间。来自100个不同线程的100个调用者可以存储对mRootData的引用,并决定同时修改它!类似地,在调用SetRootData之后,调用者可以保留对根数据的引用,并随时访问它,甚至不会注意到(最终除了数据损坏或死锁......)。

你有以下选择(除了祈祷客户很好,不要对你可怜的单身人士做坏事; - )

  • 在getter和setter中创建mRootData的深层副本。这样可以将数据限制在单例中,可以用锁来保护数据。 GetRootData的OTOH呼叫者获取数据的快照,并且后续更改对他们不可见 - 这可能是您可能接受的,也可能是不可接受的。
  • 重写RootData以使其线程安全,然后单例不再需要关心线程安全(在其当前设置中 - 如果它有其他数据成员,则图片可能不同)。

Update2

  • 或完全删除getter和setter(可能与将其他类中的数据处理方法移动到单例中一起使用,其中这些方法可以被互斥锁正确保护)。这将是最简单和最安全的,除非您绝对需要其他方直接访问mRootData

答案 1 :(得分:0)

1。)是的,互斥是必要的。虽然在任何时候都只存在一个类的实例,但多个线程仍然可以同时在该实例上调用process()(除非您设计应用程序以便永远不会发生)。

2.。)无论何时使用该值,都应使用互斥锁保护它。

但是,您没有在上面的类声明中提到GetRootData和SetRootData。这些是私有的(仅在类中使用来访问数据)还是公共的(允许其他代码直接访问数据)?

如果您需要通过公开GetRootData()函数来提供对数据的外部访问,那么您需要返回一个副本,或者您的调用者可以在锁定释放后存储引用并操作数据。当然,他们对数据所做的更改不会反映在单例内,这可能不是你想要的。