什么时候这个锁定然后锁定再次重构一个坏主意?

时间:2014-10-08 09:24:30

标签: c++ multithreading c++11

我有两个同级班,AB,我想重构它们,以便AB的父级,所以{{ 1}}可以共享B的代码。但是,对于一个关键功能,这个重构意味着锁定互斥锁两次而不是一次。有什么理由没有?

A类

A

B类

class A{
     std::map<std::string, int> values;
     std::mutex mutex;
 public:
     //init and access functions elided

     void foo(std::string key, int v){
         auto i = values.find(key);
         if(i == values.end())return; //Actually report error
         {
             std::lock_guard<std::mutex> lock(mutex);
             i->second = v;
         }
     }
};

我想写的B类替代方案是:

class B{
    std::map<std::string, int> values;
    std::map<std::string, std::vector<int> > history;
    std::mutex mutex;
public:
    //init and access functions elided

    void foo(std::string key, int v){
        auto i = values.find(key);
        if(i == values.end())return; //Actually report error
        auto i2 = history.find(key);
        if(i2 == history.end())return; //Actually report error
        {
            std::lock_guard<std::mutex> lock(mutex);
            i->second = v;
            i2->second.push_back(v);
        }
    }
 };

访问函数也使用互斥锁来锁定它们的读取。出于这个问题的目的,假设class C:public A{ std::map<std::string, std::vector<int> > history; public: //init and access functions elided void foo(std::string key, int v){ A::foo(key,v); auto i2 = history.find(key); if(i2 == history.end())return; //Actually report error { std::lock_guard<std::mutex> lock(mutex); i2->second.push_back(v); } } }; 被调用比这些类中的任何其他函数更多。我们还可以假设所有对foo()的调用都是序列化的;互斥体用于其他使用访问函数的线程。

Q值。将一个互斥锁拆分为两个串行锁,不能添加任何新的死锁潜力吗?

Q值。代码重复与A类和B类有没有更大的代码气味,或者来自&#34;隐藏&#34;基类调用中的额外互斥锁?

Q值。与我在foo()中执行的其他操作相比,锁定两次的额外开销是否微不足道?即我猜测插入地图和向量的时间至少是锁定互斥锁的10倍。

Q值。 foo()现在允许读取与class C读取不同步的values(即,如果另一个线程在history中间抓住锁定)。如果结果证明是一个问题,那就回到&#34;复制A类和B类的代码。唯一的设计选择?

2 个答案:

答案 0 :(得分:1)

这个替代方法怎么样,它会添加一个返回锁的foo_impl函数,因此可以在C::foo中重复使用:

class A
{
  std::map<std::string, int> values;
  std::mutex mutex;
public:
  //init and access functions elided

  void foo(std::string key, int v)
  {
    foo_impl(key, v);
  }

protected:
  std::unique_lock<std::mutex> foo_impl(std::string key, int v)
  {
    auto i = values.find(key);
    if (i == values.end()) return {}; //Actually report error
    std::unique_lock<std::mutex> lock(mutex);
    i->second = v;
    return lock;
  }
};

class C : public A
{
  std::map<std::string, std::vector<int> > history;
public:
  //init and access functions elided

  void foo(std::string key, int v)
  {
    auto i2 = history.find(key);
    if (i2 == history.end()) return; //Actually report error
    if (auto lock = A::foo_impl(key,v))
      i2->second.push_back(v);
  }
};

这可确保A::valuesC::history的更新在单个锁定下完成,因此A::values无法在原始C::foo中的两个锁之间再次更新。{ / p>

答案 1 :(得分:-1)

我不明白为什么你认为有必要锁两次,这看起来像你想做的?

class A{
  std::map<std::string, int> values;
  std::mutex mutex;

protected:
  void foo_unlocked(std::string key, int v){
    auto i = values.find(key);
    if(i != values.end())
      i->second = v;
  }
};

class C:public A{
  std::map<std::string, std::vector<int> > history;
public:
  //init and access functions elided

  void foo(std::string key, int v){
    auto i2 = history.find(key);
    if(i2 == history.end())
      return; //Actually report error

    std::lock_guard<std::mutex> lock(mutex);
    i2->second.push_back(v);
    foo_unlocked(key, v);  // do the operation in A but unlocked...
 }

};