Использование условной переменной pthread с rwlock - PullRequest
9 голосов
/ 23 апреля 2010

Я ищу способ использования pthread rwlock структуры с условиями подпрограммами в C ++.

У меня два вопроса:

Во-первых: как это возможно, а если нет, то почему?

Второе: почему в текущей POSIX-версии не реализовано такое поведение?

Чтобы понять мою цель, я объясню, что будет моим использованием: у меня есть модель производитель-потребитель, имеющая дело с одним общим массивом. Потребитель будет cond_wait, когда массив пуст, но rdlock при чтении некоторых элементов. Производитель будет блокировать при добавлении (+ сигнал) или удалении элементов из массива.

Преимущество использования rdlock вместо mutex_lock заключается в улучшении производительности: при использовании mutex_lock блокируются несколько считывателей, тогда как при использовании rdlock не блокируются несколько читателей.

Ответы [ 6 ]

6 голосов
/ 23 апреля 2010

Я предполагаю, что под «условиями» вы подразумеваете «условные переменные». Это разные вещи.

Нет, вы не можете использовать rwlock при ожидании условной переменной. Я не могу ответить на вопрос «почему», но именно так POSIX решил это сделать. Возможно, просто для простоты.

Однако вы все равно можете получить желаемое поведение, создав собственный класс rwlock, используя только мьютекс и 2 условные переменные, без использования POSIX rwlock:

getReadLock():
     lock(mutex)
     while(array.empty())
         wait(readersCondVar, mutex)
     readers++;
     unlock(mutex)       

releaseReadLock():
     lock(mutex)
     if (--readers == 0)
           broadcast(writerCondVar, mutex) // or signal, if only 1 producer
     unlock(mutex)

readerThread:
     forever() {
         getReadLock()
         read()
         releaseReadLock()
      }

getWriteLock():
     lock(mutex)
     while(readers) {
         wait(writerCondVar, mutex)
     }

releaseWriteLock():
     broadcast(readersCondVar, mutex)
     unlock(mutex)

writerThread():
      forever() {
         getWriteLock()
         write()
         releaseWriteLock()
      }

Простой и делает то, что вы хотите.

1 голос
/ 11 сентября 2013

У меня такое же требование, как и вам.

Вот мое решение:

Wrap pthread_rwlock_t

class rwlock
{
public:
    rwlock()
    {
        pthread_rwlock_init(&_lock, nullptr);
    }

    ~rwlock()
    {
        pthread_rwlock_destroy(&_lock);
    }

    void read_lock()
    {
        pthread_rwlock_rdlock(&_lock);
    }

    void write_lock()
    {
        pthread_rwlock_wrlock(&_lock);
    }

    bool try_read_lock()
    {
        return pthread_rwlock_tryrdlock(&_lock) == 0;
    }

    bool try_write_lock()
    {
        return pthread_rwlock_trywrlock(&_lock) == 0;
    }

    void lock()
    {
        read_lock();
    }

    void try_lock()
    {
        try_read_lock();
    }

    void unlock()
    {
        pthread_rwlock_unlock(&_lock);
    }

private:
    pthread_rwlock_t _lock;
};

Использование

rwlock lock;

std::condition_variable_any cond;

bool ready = false;

Производитель

lock.write_lock();
...
if (!ready) {
    ready = true;
    cond.notify_all();
}
lock.unlock();

Потребитель

std::unique_lock<rwlock> lock_(lock);
while (!ready) {
    cond.wait(lock_, []{ return ready; });
}
...
ready = false;
1 голос
/ 02 марта 2011

C ++ 0x получает поддержку многопоточности, и эта поддержка включает в себя новый тип с именем condition_variable_any:

class condition_variable_any
{
public:
    condition_variable_any();
    ~condition_variable_any();

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

    void notify_one();
    void notify_all();

    template <class Lock>
        void wait(Lock& lock);
    template <class Lock, class Predicate>
        void wait(Lock& lock, Predicate pred);

    template <class Lock, class Clock, class Duration>
        cv_status
        wait_until(Lock& lock,
                   const chrono::time_point<Clock, Duration>& abs_time);

    template <class Lock, class Clock, class Duration, class Predicate>
        bool
        wait_until(Lock& lock,
                   const chrono::time_point<Clock, Duration>& abs_time,
                   Predicate pred);

    template <class Lock, class Rep, class Period>
        cv_status
        wait_for(Lock& lock,
                 const chrono::duration<Rep, Period>& rel_time);

    template <class Lock, class Rep, class Period, class Predicate>
        bool
        wait_for(Lock& lock,
                 const chrono::duration<Rep, Period>& rel_time,
                 Predicate pred);
};

Здесь есть объяснение того, как реализовать condition_variable_any:

http://www.open -std.org / ОТК1 / SC22 / wg21 / документы / документы / 2007 / n2406.html # gen_cond_var

но по этой ссылке он называется gen_cond_var. Волшебная вещь в условии condition_variable_any заключается в том, что он будет ожидать чего-либо, имеющего члены lock () и unlock (). Если у вас есть condition_variable_any, то все, что вам нужно, это рулок. Ссылка выше также представляет shared_mutex и shared_lock и показывает пример кода, который делает то, что вы хотите:

std::tr2::shared_mutex mut;
std::gen_cond_var cv;

void wait_in_shared_ownership_mode()
{
    std::tr2::shared_lock<std::tr2::shared_mutex> shared_lk(mut);
    // mut is now shared-locked
    // ...
    while (not_ready_to_proceed())
        cv.wait(shared_lk);  // shared-lock released while waiting
    // mut is now shared-locked
    // ...
}   // mut is now unlocked

void wait_in_unique_ownership_mode()
{
    std::unique_lock<std::tr2::shared_mutex> lk(mut);
    // mut is now unique-locked
    // ...
    while (not_ready_to_proceed())
        cv.wait(lk);  // unique-lock released while waiting
    // mut is now unique-locked
    // ...
}   // mut is now unlocked

Приведенный выше документ несколько устарел. Более свежая реализация и описание shared_mutex / shared_lock здесь:

http://howardhinnant.github.io/shared_mutex http://howardhinnant.github.io/shared_mutex.cpp

Все это реализовано поверх pthreads POSIX. Я надеюсь получить информацию о разделяемой блокировке в техническом отчете C ++ (tr2), но, конечно, на это нет никаких гарантий.

0 голосов
/ 11 сентября 2012

Существует несколько RWLocks , реализованных поверх мьютексов и кондаров.Выберите любой и добавьте несколько condvars для ваших пользовательских потребностей.

0 голосов
/ 02 марта 2011

Для решения проблемы, о которой сообщает Судный день, необходимо снова проверить состояние после grab_mutex и до wait_on_condition:

consumer() {

  get_readlock();
  if(array_empty()) {
    release_readlock();
    grab_mutex();
    if(array_empty()) {
       wait_on_condition();
    }
    release_mutex();
    get_readlock();
  }
  process_elements();
  release_readlock();
}  
0 голосов
/ 23 апреля 2010

Для того, что вы хотите, вам нужно иметь только 1 набор rwlock и 1 набор переменных mutex / cond, псевдокод (хотя вам понадобятся обычные циклы для переменных posix cond)

consumer() {

  get_readlock();
  if(array_empty()) {
    release_readlock();
    grab_mutex();
    wait_on_condition();
    release_mutex();
    get_readlock();
  }
  process_elements();
  release_readlock();
}

producer()
{
  get_writelock();
  get_mutex();
  insert_elements();
  signal_condition();
  release_mutex();
  release_writelock();
}

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

...