2017-11-17 5 views
4

私はブロッキングキューを持っています(実装を変更するのは本当に難しいでしょう)。特に、popメソッドは、キューが空であり、pushが実行されるとすぐにブロックを解除する必要がある場合はブロックする必要があります。テストのために以下の擬似C++ 11のコードを参照してください:明らかブロッキングキューの実際のブロックをテストする方法

BlockingQueue queue; // empty queue 

thread pushThread([] 
{ 
    sleep(large_delay); 
    queue.push(); 
}); 

queue.pop(); 

を、全体のスレッドpushThreadが呼び出され実行され、pop前に終了していることが起こる可能性があるため、それは、完璧ではない、遅延が大きい場合であっても遅延が大きくなればなるほど、テストが終わるのを待たなければなりません。

pushが呼び出される前にpopが実行され、ブロックされていることを正しく確認するには、pushが返されますか?

+0

通常、他のスレッドにはスケジューラがスリープさせたスレッドと同じように見えるため、標準のC++では使用できません。実際に言えば、50ミリ秒程度待つだけで十分です。 – MikeMB

答えて

0

std::condition_variableを使用してこれを達成できます。 http://en.cppreference.com/w/cpp/thread/condition_variable

EDIT:cppreference.comのヘルプページには、実際にあなたが探している正確に何であるべき非常に素晴らしいcosumerプロデューサーの一例を示し、実際cppreference.comのドイツ語版はhttp://de.cppreference.com/w/cpp/thread/condition_variable :-)さらに良い例があります

+0

あなたはどこで待っていて、どこに信号を送るでしょうか? – Nick

+0

2つのスレッドを起動します.1つは 'pop()'と 'push'です。 'pop()'の後に待機し、 'push()'の後にシグナルを送ります。 'pop()'スレッドの後に 'push()'スレッドを必ず開始してください。 –

+1

@SamerTufail私はあなたが問題を本当に解決することなく複雑さを加えたと思います。あなたのソリューションのpopの後にpushを確実に呼び出す方法を教えてください。 – Nick

1

BlockingQueueに余分な状態やインターフェースを追加しなければ、これは可能ではないと思います。

証明は次のようになります。 popで読み取りスレッドがブロックされるまで待つ必要があります。しかし、それとスレッドとを区別する方法はありませんpopを実行しようとしている。これは、popへの呼び出しの直前または直後に何を置いても同じです。

本当にこれを100%の信頼性で修正したい場合は、キューのミューテックスによって保護された状態をキュー内に追加する必要があります。つまり、「誰かが待っている」ということです。 popコールは、mutexをアトミックにリリースする直前にその状態を更新しなければならず、内部条件変数でスリープ状態になります。 pushスレッドはmutexを取得し、「誰かが待っている」まで待つことができます。ここでビジーループを回避するには、条件変数を再度使用する必要があります。

このマシンはすべてキュー自体と同じくらい複雑なので、テストしたいと思うかもしれません。このようなマルチスレッドコードは、「コードカバレッジ」のような概念であり、単体テストのテスト - ちょっと分解してください。可能な操作のインターリーブが多すぎるだけです。

実際には、私はおそらくあなたの元の睡眠のアプローチに行くでしょう。

1
template<class T> 
struct async_queue { 
    T pop() { 
    auto l = lock(); 
    ++wait_count; 
    cv.wait(l, [&]{ return !data.empty(); }); 
    --wait_count; 
    auto r = std::move(data.front()); 
    data.pop_front(); 
    return r; 
    } 
    void push(T in) { 
    { 
     auto l = lock(); 
     data.push_back(std::move(in)); 
    } 
    cv.notify_one(); 
    } 
    void push_many(std::initializer_list<T> in) { 
    { 
     auto l = lock(); 
     for (auto&& x: in) 
     data.push_back(x); 
    } 
    cv.notify_all(); 
    } 
    std::size_t readers_waiting() { 
    return wait_count; 
    } 
    std::size_t data_waiting() const { 
    auto l = lock(); 
    return data.size(); 
    } 
private: 
    std::queue<T> data; 
    std::condition_variable cv; 
    mutable std::mutex m; 
    std::atomic<std::size_t> wait_count{0}; 
    auto lock() const { return std::unique_lock<std::mutex>(m); } 

}; 

またはsomesuch。 readers_waiting上のプッシュスレッド、ビジーウェイトで

それはあなたがロックを持っていると、ロックがアンロックされる前にcv.wait内にある時点で1

を通過するまで。 pushを実行してください。

理論的には無限に遅い読者のスレッドがcv.waitに得ている可能性があり、まだあなたがpushを呼び出す時点で最初のラムダを評価することが、無限に遅い読者のスレッドがブロックされたものとは違いはありません...

しかし、スレッドの起動が遅いなどの問題があります。

デバッグ以外の目的でreaders_waitingdata_waitingを使用するのは、通常コードの匂いです。

+0

OT: 'unique_lock 'または' lock_guard'を「最も厄介な構文解析」に関する個人的(悪い)経験から除外していますが、それは与えられたコードには関係ありません。 –

関連する問題