Как сделать мьютекс с приоритетами или RW-lock с эксклюзивным чтением?

Рейтинг: 2Ответов: 1Опубликовано: 29.09.2014

Доброго времени суток!

Есть некоторая структура данных, к которой обращается малое число поставщиков данных и в несколько раз больше число потребителей. И поставщик данных, и потребитель требует эксклюзивной блокировки для своей работы, но если использовать обычный mutex, то в результате получается перекос в сторону потребителей, и поставщики данных начинают терять производительность.

Фактически необходимо три уровня приоритетов: shared - read-only операции, которые могут выполняться реально одновременно, не мешая друг другу; consume-exclusive - низкопроиоритетные операции, требующие эксклюзивной блокировки; produce-exclusive - высокоприоритетные операции, требующие эксклюзивной блокировки.
В принципе shared можно даже не выделять, а использовать consume-exclusive уровень для этих задач, так как они очень маленькие по ресурсоемкости (много меньше, чем другие операции).

Первой мыслью было взять boost::shared_mutex и внутри shared-блокировки использовать обычный mutex для разделения читателей, но тогда получится, что если на этом мутексе заблокируется читатель, и в этот момент придет писатель и начнет ждать уникальную блокировку, то вначале все равно отработает читатель, и только после того, как он освободит shared-блокировку, писатель сможет приступить к работе.

Как-либо менять приоритеты потоков или воздействовать на планировщика ОС нельзя, так как эти потоки еще много другой работы выполняют, и будет очень плохо, если у них будут как-то меняться приоритеты.

Подскажите, пожалуйста, как можно реализовать подобную функциональность?

Ответы

▲ 2

Вариант решения.

Читающие поток:

  • лок на мьютекс;
  • пока переменная не позволяет, ждем condition на этот мьютекс;
  • выполняем свои секретные действия;
  • делаем notify_one, чтобы отпустить кого-то из condition;
  • анлочим мьютекс.

Пишущие потоки:

  • устанавливаем значение атомарной переменной в блокирующее читающих;
  • лочим мьютекс;
  • устанавливаем значение атомарной переменной в не блокирующее (теперь оно не очень нужно);
  • выполняем секретные пишущией действия;
  • выполняем notify_one;
  • анлочим мьютекс.

В такой схеме гарантированно пишущие будут иметь вытесняющий приоритет перед читающими.

Реализация:

template<typename T>
class SomethingDoing : public boost::noncopyable
{
private:
    typedef boost::mutex::scoped_lock scoped_lock;

    boost::mutex _mutex_data_access;
    boost::condition_variable _cond;

    atomic_bool _readaccess;

public:
    SomethingDoing ()
    {
        _readaccess = true;
    }

    void Write()
    {
        _readaccess = false;
        scoped_lock lock(_mutex_data_access);
        _readaccess = true;
        /*DO YOUR CODE HERE*/ 
        _cond.notify_one();
    }
    void Read()
    {
        scoped_lock lock(_mutex_data_access);
        while(!_readaccess)
        {
            _cond.wait(lock);
        }
        /*DO YOUR STUFF*/
        _cond.notify_one();
    }

};

UPD v2: (не могу писать комментарии больше) писателю и читателю требуется _cond.notify_one(); в конце, чтобы отпустить ожидающих в condition читателей, если такие будут.