Использование более одного мьютекса с условной переменной - PullRequest
1 голос
/ 12 августа 2011

Есть ли механизм, чтобы условная переменная использовала несколько мьютексов? Я в Linux и Pthreads в C ++.

В приложении мне нужно, чтобы два мьютекса (вместо одного) были атомарно получены и освобождены pthread_cond_wait (), но функция принимает только один.

У меня есть класс с именем BlockingManager, и у него есть метод:

blockMeFor( pthread_cond_t* my_cond, pthread_mutex_t* my_lock, set<int> waitees);

и я использую его, предполагая, что он получает / освобождает мьютекс точно так же, как pthread_cond_wait.

Проблема в том, что для реализации blockingManager мне также нужен внутренний мьютекс, и оба этих мьютекса должны быть получены и освобождены атомарно.

Здесь как-то связано обсуждение, в котором говорится, что ожидание более чем одного мьютекса приводит к неопределенному поведению. http://sourceware.org/ml/libc-help/2011-04/msg00011.html

Модель производителя / потребителя для стоящей передо мной проблемы:

У нас есть несколько клиентов. У каждого клиента есть несколько задач. Каждая задача, вероятно, имеет несколько предварительных условий (среди задач одного и того же клиента или других клиентов). У каждого клиента есть один потребительский поток. Задачи назначаются клиентам из одного потока производителя. Вновь назначенная задача может быть выполнена до выполнения предыдущих задач. В некоторые моменты может не быть задачи, но если есть задача, по крайней мере, одна должна быть выполнена. (Это должно сохранить работу)

Я использую один condvar для каждого потока пользователя, и он будет блокироваться, если для этого потока не будет выполнено никаких задач. Кондвар может быть сигнализирован либо

  • Поток производителя, назначающий новую задачу.

  • Еще один потребительский поток, завершающий задачу.

Я использую один мьютекс на каждого потребителя для защиты общих структур данных между производителем и потребителем. И один мьютекс (внутренний мьютекс) для защиты общих структур данных между несколькими потребителями.

1 Ответ

5 голосов
/ 13 августа 2011

В C ++ 11 (если ваш компилятор поддерживает это) вы можете использовать std::lock для блокировки двух мьютексов одновременно (без взаимоблокировки).Вы можете использовать это для создания Lock2 класса, который ссылается на два мьютекса.И тогда вы можете использовать std::condition_variable_any для ожидания на Lock2.Все это может выглядеть примерно так:

#include <mutex>
#include <condition_variable>

std::mutex m1;
std::mutex m2;
std::condition_variable_any cv;

class Lock2
{
    std::mutex& m1_;
    std::mutex& m2_;

public:
    Lock2(std::mutex& m1, std::mutex& m2)
        : m1_(m1), m2_(m2)
    {
        lock();
    }

    ~Lock2() {unlock();}

    Lock2(const Lock2&) = delete;
    Lock2& operator=(const Lock2&) = delete;

    void lock() {std::lock(m1_, m2_);}
    void unlock() {m1_.unlock(); m2_.unlock();}
};

bool not_ready() {return false;}

void test()
{
    Lock2 lk(m1, m2);
    // m1 and m2 locked
    while (not_ready())
        cv.wait(lk);  // m1 and m2 unlocked
    // m1 and m2 locked
}  // m1 and m2 unlocked

Если ваш компилятор еще не поддерживает эти инструменты, вы можете найти их в boost.

...