Безопасна ли эта реализация очереди блокировки? - PullRequest
5 голосов
/ 08 июля 2011

Я пытаюсь реализовать очередь, которая блокирует операцию Pop, если она пуста, и разблокируется, как только новый элемент помещается.Боюсь, у меня может быть какое-то состояние гонки;Я попытался взглянуть на какую-то другую реализацию, но большинство из найденных мной было сделано в .NET, и несколько найденных мной C ++ слишком сильно зависели от других библиотечных классов.

template <class Element>
class BlockingQueue{
    DRA::CommonCpp::CCriticalSection    m_csQueue;
    DRA::CommonCpp::CEvent              m_eElementPushed;
    std::queue<Element>                 m_Queue;
public:
    void Push( Element newElement ){
        CGuard g( m_csQueue );
        m_Queue.push( newElement );
        m_eElementPushed.set();
    }
    Element Pop(){
        {//RAII block
            CGuard g( m_csQueue );
            bool wait = m_Queue.empty();
        }
        if( wait )
            m_eElementPushed.wait();
        Element first;
        {//RAII block
            CGuard g( m_csQueue );
            first = m_Queue.front();
            m_Queue.pop();
        }
        return first;
    }
};

Должны быть некоторые объяснения:

  • CCriticalSection - это оболочка для критического раздела Windows, методы Enter и Leave являются частными, а CGuard - его единственный друг
  • CGuard - это оболочка RAII для CCriticalSection, входит в критический раздел на конструкторе, оставляетэто на деструкторе
  • CEvent - оболочка для события Windows, wait использует функцию WaitForSingleObject
  • Я не против, чтобы элементы передавались по значению, они являются маленькими объектами
  • Я не могу использовать Boost, только что-то для Windows (как я уже делал с CEvent и CGuard)

Боюсь, что при использовании Pop () может возникнуть какой-то странный сценарий состояния гонки.,Что вы, ребята, думаете?

ОБНОВЛЕНИЕ : Поскольку я работаю над Visual Studio 2010 (.NET 4.0), я в конечном итоге использовал класс unbounded_buffer, предоставляемый средой выполнения C ++.Конечно, я обернул его в класс с помощью идиомы указателя на реализацию (Chesire Cat) на тот случай, если мы решим изменить реализацию или нужно перенести этот класс в другую среду

Ответы [ 2 ]

9 голосов
/ 08 июля 2011

Это не потокобезопасно:

    {//RAII block
        CGuard g( m_csQueue );
        bool wait = m_Queue.empty();
    }
    /// BOOM! Other thread ninja-Pop()s an item.
    if( wait )
        m_eElementPushed.wait();

Обратите внимание на расположение комментария BOOM. На самом деле, другие места также мыслимы (после if). В любом случае последующие вызовы front и pop не будут выполнены.

4 голосов
/ 08 июля 2011

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

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

...