Поведение RAII boost::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 массив блокировок в Container ctor и delete на его dtor, но я думаю, что это противоречит самой идиоме RAII.

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


person Patrizio Bertoni    schedule 13.09.2016    source источник


Ответы (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);
    }
}
person Vittorio Romeo    schedule 13.09.2016
comment
Зачем писать свое? Стандартная библиотека имеет множество функций (от std::lock до defer_lock_t). Boost делает его еще более мощным с интерфейсом диапазона итераторов (см. Мой ответ). Обратите внимание, что это будет намного надежнее, потому что ваша реализация, естественно, подвержена взаимоблокировкам. - person sehe; 13.09.2016
comment
@sehe: Справедливое замечание - мой ответ направлен на то, чтобы показать OP, как решить проблему защиты нескольких блокировок общим способом, который можно применять для аналогичных проблем, и показать, как реализовать собственную оболочку RAII. - person Vittorio Romeo; 13.09.2016

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

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);
  }
}
person Jeremy    schedule 13.09.2016

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

Жить на 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);
}
person sehe    schedule 13.09.2016