Поведение RAII::scoped_lock

Из класса 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 привязан к процессору, а контролируемые объекты изменяются откуда-то еще, я хотел бы просто сделать циклический scoped_lock в начале main_method, для того, чтобы заблокировать все и как можно скорее, такие как

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 цикл заканчивается

Я пытался new массив блокировок в контейнере ctor и delete это в его dtor, но я думаю, что это против самой идиомы RAII.

Что я неправильно понял, или как я мог реорганизовать всю проблему?

3 ответа

Решение

Предполагая, что ваш вопрос таков: "Как использовать RAII-подобную блокировку области действия для нескольких мьютексов одновременно?"

Затем вы можете создать свою собственную RAII-оболочку для нескольких блокировок или использовать что-то вроде охранника области видимости.

(Непроверенный псевдокод, но надеюсь, что вы поняли.)

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);
    }
}

Как насчет следующего?

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);
  }
}

Вы уже можете использовать бесплатные расширения функций из Boost Thread для атомарной блокировки коллекции отложенных блокировок:

Жить на Колиру

#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);
}
Другие вопросы по тегам