【问题标题】:boost::scoped_lock RAII behaviourboost::scoped_lock RAII 行为
【发布时间】:2016-09-13 07:29:03
【问题描述】:

从一个 Container 类,我想 lock 一个 boost::mutex 的向量,每个向量都由一个 Controlled 实例拥有(奇怪的代码设计,但是仅用于 MWE 目的)。

// std::vector<Controlled*> _vData;

void Container::main_method()
{
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
    this->processing(i);
  }

  // precondition for post_processing(): all processing() calls done
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
    this->post_processing(i);
  }
}

但由于 processing 是 cpu-bound 并且 受控 对象同时从其他地方修改,我想简单地在 @987654326 的开头做一个循环的 scoped_lock @,为了锁定一切尽快,比如

void Container::desired_main_method()
{
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
  }

  // locks destroyed here, aren't they ?

  for (int i=0; i<_vData.size(); i++)
  {
    this->processing(i);
  }

  for (int i=0; i<_vData.size(); i++)
  {
    this->post_processing(i);
  }
}

问题是,如果我很好地理解了 RAII 习语和 scoped_lock 上下文,那么在锁定 for 循环结束后,锁定将很快超出范围。

我尝试在 Container ctor 上 new 一组锁,并在其 dtor 上 delete 它,但我想这违反了 RAII 习语本身。

我误解了什么,或者我该如何重构整个问题?

【问题讨论】:

    标签: c++ boost scope mutex scoped-lock


    【解决方案1】:

    假设您的问题是:“如何同时为多个互斥锁使用类似 RAII 的作用域锁?”

    然后,您可以为多个锁创建自己的 RAII 包装器,也可以使用 scope guard 之类的东西。

    (未经测试的伪代码,但希望你明白。)

    template <typename TIterator>
    class multiple_lock_guard
    {
    private:
        TIterator _begin, _end;
    
        multiple_lock_guard(TIterator begin, TIterator end)
            : _begin{begin}, _end{end}
        {
            for(auto it = _begin; it != _end; ++it)
            {
                it->_mutex.lock();
            }
        }
    
        ~multiple_lock_guard()
        {
            for(auto it = _begin; it != _end; ++it)
            {
                it->_mutex.unlock();
            }
        }
    };
    

    您可以按如下方式使用它:

    void Container::desired_main_method()
    {
        multiple_lock_guard mlg(std::begin(_vData), std::end(_vData));
    
        for(int i = 0; i < _vData.size(); i++)
        {
            this->processing(i);
        }
    
        for(int i = 0; i < _vData.size(); i++)
        {
            this->post_processing(i);
        }
    }
    

    【讨论】:

    • 为什么要自己写?标准库有很多特性(围绕std::lockdefer_lock_t)。 Boost 通过 iterator-range 接口使其更加强大(请参阅我的答案)。请注意,它会更加健壮,因为您的实现自然容易死锁
    • @sehe:公平点 - 我的回答旨在向 OP 展示如何以可应用于类似问题的通用方式解决其“多锁保护”问题,并展示如何实现您自己的 RAII包装器。
    【解决方案2】:

    下面的呢?

    void Container::desired_main_method()
    {
      std::vector<boost::mutex::scoped_lock> locks;
    
      for (int i=0; i<_vData.size(); i++)
      {
        locks.emplace_back(_vData.at(i)->_mutex);
      }
    
      for (int i=0; i<_vData.size(); i++)
      {
        this->processing(i);
      }
    
      for (int i=0; i<_vData.size(); i++)
      {
        this->post_processing(i);
      }
    }
    

    【讨论】:

      【解决方案3】:

      您已经可以使用 Boost Thread 的免费函数扩展来原子地锁定延迟锁的集合:

      Live On Coliru

      #include <boost/thread.hpp>
      #include <vector>
      
      struct X {
          boost::mutex mutable mx;
      };
      
      void foo(std::vector<X> const& xs) {
      
          std::vector<boost::unique_lock<boost::mutex> > locks;
          for (auto& x : xs) {
              locks.emplace_back(x.mx, boost::defer_lock);
          }
      
          boost::lock(locks.begin(), locks.end());
      
          // all locks held
      }
      
      int main() {
          std::vector<X> xs(10);
          foo(xs);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        • 2011-02-22
        • 2014-08-06
        • 2015-02-23
        • 2010-11-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多