2017-04-10 6 views
1

thread1機能は出力が1 2 1 2が、私のUbuntuでその1 1 1ある私のMacではC++のスレッドが

#include <iostream> 
    #include <fstream> 
    #include <thread> 
    #include <condition_variable> 
    #include <queue> 

    std::condition_variable cv; 
    std::mutex mu; 
    std::queue<int> queue; 
    bool ready; 

    static void thread1() { 
     while(!ready) {std::this_thread::sleep_for(std::chrono::milliseconds(10));} 

     while(ready && queue.size() <= 4) { 
       std::unique_lock<std::mutex> lk(mu); 
       cv.wait(lk, [&]{return !queue.empty();}); 

       queue.push(2); 
     } 
    } 

    int main() { 
     ready = false; 
     std::thread t(thread1); 

     while(queue.size() <= 4) { 
      { 
       std::lock_guard<std::mutex> lk(mu); 
       queue.push(1); 
      } 

      ready = true; 
      cv.notify_one(); 
     } 

     t.join(); 

     for(int i = 0; i <= queue.size(); i++) { 
       int a = queue.front(); 
       std::cout << a << std::endl; 
       queue.pop(); 
     } 

     return 0; 
} 

を実行し得るように見えることはありません実行されません。私はg++ -std=c++11 -pthread -o thread.out thread.cpp && ./thread.outでコンパイルしています。何か不足していますか?

+0

メインループが再度ロックする前に、スレッド1がミューテックスを取得できる保証はありません。それはマルチスレッド化の仕方であり、予測できないことがあります。 –

+0

しかし私は、thread1の条件変数が、mainとthread1の両方の待機と同期でmutexをロックすると考えました。同期を実行して順番に実行するにはどうすればよいですか? – Seph

+0

あなたは競争状態にあります。 'thread1'は' queue.size() 'を実行でき、' main'は 'queue.push(1)'を実行し、動作は未定義です。 – GManNickG

答えて

0

別の条件変数の別の述語で2番目のスレッドを待機させることで、これを解決できました。 queue.size()がスレッドセーフであるかどうかはわかりません。

#include <iostream> 
#include <fstream> 
#include <thread> 
#include <condition_variable> 
#include <queue> 

std::condition_variable cv; 
std::condition_variable cv2; 
std::mutex mu; 
std::queue<int> queue; 
bool tick; 
bool tock; 

static void thread1() { 
while(queue.size() < 6) { 
    std::unique_lock<std::mutex> lk(mu); 
    cv2.wait(lk, []{return tock;}); 

    queue.push(1); 

    tock = false; 
    tick = true; 
    cv.notify_one(); 
} 
} 

int main() { 
tick = false; 
tock = true; 
std::thread t(thread1); 

while(queue.size() < 6) { 
    std::unique_lock<std::mutex> lk(mu); 
    cv.wait(lk, []{return tick;}); 

    queue.push(2); 

    tick = false; 
    tock = true; 
    cv2.notify_one(); 
} 

t.join(); 

while(!queue.empty()) { 
    int r = queue.front(); 
    queue.pop(); 

    std::cout << r << std::endl; 
} 

return 0; 
} 
2

この:

for(int i = 0; i <= queue.size(); i++) { 
    int a = queue.front(); 
    std::cout << a << std::endl; 
    queue.pop(); 
} 

は未定義の動作です。 forループは0からsizeになり、size+1回実行されます。私はあなたがキューのためのより多くの慣用的なスタイルでこれを書くことを示唆している:

while(!queue.empty()) { 
    int a = queue.front(); 
    std::cout << a << std::endl; 
    queue.pop(); 
} 

私はcoliruでこれを実行すると、私は* nixのマシンのいくつかの種類を実行すると想定され、私は4 1の取得:http://coliru.stacked-crooked.com/a/8de5b01e87e8549eを。

この場合も、各スレッドを強制的に一定の時間実行するよう指定していません。いずれかの方法で、キューがサイズ4に達する不変条件を引き起こす(*しようとする)だけです。私たちが実行したマシンでスレッド2がミューテックスを取得することは決してありません。

この例は、仕事を増やしたり、(教育的目的のためだけに)さまざまな時点で遅延を追加すると、さらに面白くなります。 2つのスレッドが実際に作業していることをシミュレートします。複数の場所でスリープ状態を追加すると、2つのスレッドが交互に表示されますが、どこに追加するかに応じてスレッドブレークに4つの要素の不変量が表示されることがあります。

※キュー上の4要素不変であっても、実際は不変ではありません。 両方のスレッドがキューに3つの要素があるときと全く同じ瞬間にwhile条件を渡すことは可能です(ただし、ほとんどありません)。最初にロックを取得してプッシュし、次にもう一方を取得します。キューには5つの要素があります。 (あなたが見ることができるように、非同期プログラミングは難しい)。特に、これが機能するためには、ロックを持っているときにキューサイズを確認する必要があります。