2017-05-08 9 views
2

私は、10個のスレッドをベクトルにプッシュするプログラムを用意しています。各スレッドは、終了する前に文字を5回印刷するはずです第1のスレッド、第2のスレッドのための「B」など)。私はそれらを一度に(detach()を使用して)実行するか、一度に1つずつ(join()を使用して)実行させることができます。今私はミューテックスを使用して一度に印刷できるスレッドの数を2に制限したいと思っています。私はミューテックスを宣言してロックをかけることができましたが、このような制限をどのように適用するのか分かりません。誰でもどのように進行するかについてのアイデアはありますか?ミューテックスを使用して一度に実行するスレッドの数を制限する2

deque<int> q ; 
mutex print_mutex ; 
mutex queue_mutex ; 
condition_variable queue_cond ; 

void begin(int num) { 
    unique_lock<mutex> ul {queue_mutex}; 
    q.emplace_back(num); 
    queue_cond.wait(ul,[num]{ 
     return q.front() == num; }); 
    q.pop_front(); 
    cout << num << " leaves begin " << endl ; 
} 

void end (int num) { 
    lock_guard<mutex>lg{queue_mutex}; 
    queue_cond.notify_all(); 
    cout << num << " has ended " << endl ; 
} 

void run(int num, char ch) { 
    begin(num); 
    for (int i = 0; i < 5; ++i) { 
     { 
      lock_guard<mutex> lg { print_mutex }; 
      cout << ch << endl << flush ; 
     } 
     sleep_for(milliseconds(250)); 
    } 
    end(num); 
} 

int main() { 
    vector<thread>threads {}; 
    for (int i = 0; i < 10; ++i) { 
     threads.push_back(thread{run,i,static_cast<char>(65+i)}); 
     threads.at(i).join(); 
    } 
} 
+1

実行中のスレッドの数を数えるには、整数が必要です。 –

+1

'detach()'と 'join()'を使って配列を制御するのは間違っています。 'main'の' for'ループは単にスレッドを直列化します。それは無意味です。ループを使用して** all **のスレッドを作成し、その後にスレッドの** **すべてを結合する別々のループを作成します。次に、スレッドがどのようにやり取りするかを理解します。 'detach()'は使わないでください。それは間違いなくあなたが必要とするものではありません。 –

+0

@PeteBecker偉大なキャッチ、それは私の問題でした!優れた説明。私はより良いロック条件を追加しようとしていましたが、別のループに結合を分離するまで何も変わっていませんでした。先に進んで答えたいと思ったら、私はそれを受け入れることができます。 – gmooney8

答えて

3

あなたはすでにグローバルdeque<int> qであなたのスレッドのFIFOを設定しています。だから、それを使ってみましょう。

現在、現在のスレッドが前面に来るまで実行を制限しようとしています。 beginはすぐに両端キューからそのスレッドをポップするので、バグがあります。 endに電話するときに値を削除する方がよいでしょう。ここではまず、その変化です:これは、特定の値を削除する<algorithm>からstd::findを使用しています

void end(int num) 
{ 
    { 
     lock_guard<mutex>lg{queue_mutex}; 
     cout << num << " has ended " << endl ; 
     q.erase(find(q.begin(), q.end(), num)); 
    } 
    queue_cond.notify_all(); 
} 

pop_frontを使用することもできますが、これをより一般的なものに変更する予定です。また、通知するときに条件変数をロックする必要はありません。

したがって、beginのロジックを最初の2か所に拡張することはあまりありません。ここで:

void begin(int num) 
{ 
    unique_lock<mutex> ul {queue_mutex}; 
    q.emplace_back(num); 
    queue_cond.wait(ul,[num]{ 
     auto end = q.begin() + std::min(2, static_cast<int>(q.size())); 
     return find(q.begin(), end, num) != end; 
     }); 
    cout << num << " leaves begin " << endl ; 
} 

あなたは合格するために、多くのスレッドまで可能、その2あなたが欲しいものに変更することができます。ある時点では、おそらくこのアプローチを放棄し、単一のカウンタ変数のような簡単な方法を使用して、スレッドスケジューラを使ってFIFOに強制するのではなく、どのスレッドが起きているかを管理します。そうすれば、notify_oneを使用して1つのスレッドをスリープさせ、スイッチングオーバーヘッドを減らすことができます。

とにかく最後に行うことは、スレッド生成ループからjoinを削除することです。同時性は現在beginendで管理されています。したがって、これを行うでしょう:

for (int i = 0; i < 10; ++i) { 
    threads.push_back(thread{run, i, 'A'+i}); 
} 
for (auto & t : threads) t.join(); 
+0

大きな説明、水田!私はあなたが言及したカウンターを使用して別の解決策を見つけることを巻いたが、あなたの説明はそれをはるかによく理解する。ありがとうございました! – gmooney8

+0

私が言いましたことを忘れていたことの1つは、あなたのbegin/endコードが例外セーフではないということです。あなたのスレッドが例外のために終了した場合、 'end'は決して呼び出されません。あなたはRAIIがそれを世話するようにオブジェクトの中でbegin/end呼び出しをラップすることを検討するかもしれません。 – paddy

関連する問題