web-dev-qa-db-ja.com

std :: queueがスレッドセーフではないのはなぜですか?

トピックはそれを言います。他のデータ構造のようにイテレータが関与していないのに、なぜstd :: queue(または一般に任意のキュー)が本質的にスレッドセーフではないのか理解できません。

その一般的なルールによると

  • 少なくとも1つのスレッドが書き込み中...
  • 別のスレッドが共有リソースから読み取っています

次のコード例では競合が発生しているはずです。

#include "stdafx.h"
#include <queue>
#include <thread>
#include <iostream>

struct response
{
    static int & getCount()
    {
        static int theCount = 0;
        return theCount;
    }

    int id;
};


std::queue<response> queue;

// generate 100 response objects and Push them into the queue
void produce()
{
    for (int i = 0; i < 100; i++)
    {
        response r; 
        r.id = response::getCount()++;
        queue.Push(r);
        std::cout << "produced: " << r.id << std::endl;
    }
}

// get the 100 first responses from the queue
void consume()
{
    int consumedCounter = 0;
    for (;;)
    {       
        if (!queue.empty())
        {
            std::cout << "consumed: " << queue.front().id << std::endl;
            queue.pop();
            consumedCounter++;
        }

        if (consumedCounter == 100)
            break;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{

    std::thread t1(produce);
    std::thread t2(consume);

    t1.join();
    t2.join();

    return 0;
}

すべてが正常に機能しているようです:-整合性違反なし/データ破損-消費者が要素を取得する要素の順序は正しい(0 <1 <2 <3 <4 ...)、もちろん本番。と短所。シグナリングが含まれていないため、印刷はランダムです。

9
netik

!queue.empty()を確認し、次のブロックを入力し、queue.first()にアクセスする前に、別のスレッドが唯一の要素を削除(ポップ)することを想像して、空のキューをクエリします。

次のような同期キューを使用する

_#pragma once

#include <queue>
#include <mutex>
#include <condition_variable>

    template <typename T>
    class SharedQueue
    {
    public:
        SharedQueue();
        ~SharedQueue();

        T& front();
        void pop_front();

        void Push_back(const T& item);
        void Push_back(T&& item);

        int size();
        bool empty();

    private:
        std::deque<T> queue_;
        std::mutex mutex_;
        std::condition_variable cond_;
    }; 

    template <typename T>
    SharedQueue<T>::SharedQueue(){}

    template <typename T>
    SharedQueue<T>::~SharedQueue(){}

    template <typename T>
    T& SharedQueue<T>::front()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        while (queue_.empty())
        {
            cond_.wait(mlock);
        }
        return queue_.front();
    }

    template <typename T>
    void SharedQueue<T>::pop_front()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        while (queue_.empty())
        {
            cond_.wait(mlock);
        }
        queue_.pop_front();
    }     

    template <typename T>
    void SharedQueue<T>::Push_back(const T& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.Push_back(item);
        mlock.unlock();     // unlock before notificiation to minimize mutex con
        cond_.notify_one(); // notify one waiting thread

    }

    template <typename T>
    void SharedQueue<T>::Push_back(T&& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.Push_back(std::move(item));
        mlock.unlock();     // unlock before notificiation to minimize mutex con
        cond_.notify_one(); // notify one waiting thread

    }

    template <typename T>
    int SharedQueue<T>::size()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        int size = queue_.size();
        mlock.unlock();
        return size;
    }
_

front()の呼び出しは、要素が追加されるまで待機し、基になるキューをロックして、一度に1つのスレッドのみがアクセスできるようにします。

18
user66875