0

現在、並行キューを書き込もうとしていますが、自分では説明できないセグメンテーション違反がいくつかあります。私のキューの実装は、基本的に、このサイトの最初のリストによって提供されます。

http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

このサイトには、オブジェクトがキューから並行して削除されると競合状態が発生すると書かれていますが、競合状態が発生する理由がわかりません。誰か説明してもらえますか?

編集:これはコードです:

template<typename Data>
class concurrent_queue
{
private:
    std::queue<Data> the_queue;
    mutable boost::mutex the_mutex;
public:
    void push(const Data& data)
    {
        boost::mutex::scoped_lock lock(the_mutex);
        the_queue.push(data);
    }

    bool empty() const
    {
        boost::mutex::scoped_lock lock(the_mutex);
        return the_queue.empty();
    }

    Data& front()
    {
        boost::mutex::scoped_lock lock(the_mutex);
        return the_queue.front();
    }

    Data const& front() const
    {
        boost::mutex::scoped_lock lock(the_mutex);
        return the_queue.front();
    }

    void pop()
    {
        boost::mutex::scoped_lock lock(the_mutex);
        the_queue.pop();
    }
};
4

4 に答える 4

1

アイテムを読み取ろうとしたときにキューが空だったらどうしますか?

次のユーザー コードを考えてみてください。

while(!q.empty())  //here you check q is not empty
{ 
       //since q is not empty, you enter inside the loop
       //BUT before executing the next statement in this loop body,
       //the OS transfers the control to the other thread
       //which removes items from q, making it empty!!
       //then this thread executes the following statement!
       auto item = q.front(); //what would it do (given q is empty?)
}
于 2013-01-13T17:43:07.010 に答える
0

empty を使用してキューが空でないことがわかった場合は、結果を使用する前に、別のスレッドがアイテムをポップして空にしている可能性があります。

フロントについても同様に、フロント アイテムを読み取ることができ、アイテムを使用するまでに別のスレッドによってポップされる可能性があります。

于 2013-01-13T17:39:20.813 に答える
0

empty()関数が役に立たない/危険である理由は明らかだと思います。ブロッキング キューが必要な場合は、それを削除します。

代わりに、条件変数 (boost::condition、IIRC) を追加します。プッシュ/ポップする関数は次のようになります。

void push(T data)
{
    scoped_lock lock(mutex);
    queue.push(data);
    condition_var.notify_one();
}

data pop()
{
    scoped_lock lock(mutex);
    while(queue.empty())
        condition_var.wait(lock);
    return queue.pop();
}

これは疑似的なコードであることに注意してください。ただし、これを理解できると確信しています。そうは言っても、実際のデータのコピーを避けるために unique_ptr (または C98 の場合は auto_ptr) を使用するという提案は良い考えですが、それは完全に別の問題です。

于 2013-01-13T18:18:06.447 に答える
0

@parkydr と @Nawaz からの回答は正しいですが、ここでもう 1 つ考えるべきことがあります。

何を達成しようとしていますか?

スレッド セーフなキューを使用する理由は、ときどき (あえて言いません) 間違っていることがあります。多くの場合、キューが単なる実装の詳細であるコンテキストで、キューの「外側」をロックしたいと考えています。

ただし、スレッド セーフ キューの 1 つの理由は、コンシューマ/プロデューサーの状況で、1-Nノードがデータをプッシュし、1-Mノードが取得内容に関係なくデータからポップするためです。キュー内のすべての要素は同等に扱われ、コンシューマーは何を取得するかを知らずにポップし、データの処理を開始します。そのような状況では、インターフェースはT& front(). そこにアイテムがあるかどうかわからない場合は、参照を返すべきではありません (また、並行して、外部ロックがなければ確実に確認することはできません)。

unique_ptr's (またはもちろん) を使用し、競合のない関数のみを公開することをお勧めしshared_ptrます (簡潔にするために const 関数を省略しています)。を使用std::unique_ptrするには C++11 が必要ですが、C++11 を使用できboost::shared_ptrない場合は、同じ機能を使用できます。

// Returns the first item, or an empty unique_ptr
std::unique_ptr< T > pop( );

// Returns the first item if it exists. Otherwise, waits at most <timeout> for
// a value to be be pushed. Returns an empty unique_ptr if timeout was reached.
std::unique_ptr< T > pop( {implementation-specific-type} timeout );

void push( std::unique_ptr< T >&& ptr );

exist()やなどの機能front()は当然、競合状態の犠牲者となります。なぜなら、これらの機能は、(あなたが考えている) 必要なタスクをアトミックに実行できないためです。exist()結果を受け取った時点で正しくない値を返すことがありfront()、キューが空の場合はスローする必要があります。

于 2013-01-13T18:11:42.267 に答える