Поведение 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 для атомарной блокировки коллекции отложенных блокировок:
- http://www.boost.org/doc/libs/1_61_0/doc/html/thread/synchronization.html
- к сожалению, C++17 не предлагает эту перегрузку http://en.cppreference.com/w/cpp/thread/lock
#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);
}