5

スレッドを使用した奇数偶数印刷 この質問に出くわし、 C++ での解決策について話し合いたいと思いました。奇数セマフォと偶数セマフォの 2 つのバイナリ セマフォを使用して考えられること。偶数セマフォは 1 に、奇数セマフォは 0 に初期化されます。

**T1 thread function** 
funOdd()
{  
  wait(even)  
  print odd;  
  signal(odd)  
}


**T2 thread function**
funEven()  
{  
  wait(odd)  
  print even  
  signal(even)  
}  

これに加えて、関数が数値のみを生成し、それらの数値を出力する 3 番目のスレッド T3 がある場合、理想的な設計は何ですか? 奇数が奇数の位置に配置され、偶数が偶数の位置に配置される配列を使用しました。T3 はこの配列から読み取ります。これにより、この配列に対するスレッド セーフが回避されます。T3 がインデックスを見つけられない場合は、そのインデックスが作成されるまで待機します。もう 1 つの解決策は、挿入中に T1 と T2 が使用できるミューテックスを持つキューを使用することです。

このソリューションについてコメントし、どうすればより効率的にすることができますか。

編集して問題を明確にします: 全体的な問題は、2 つのプロデューサー (T1、T2) と 1 つのコンシューマー (T3) があり、プロデューサーが相互に依存していることです。

4

17 に答える 17

0

解決策は、ミューテックスとも呼ばれる C++11 クリティカル コード セクションに基づいています。

これが作業コードであり、その後に説明が続きます。

VS2013 でテスト済みおよび作業中:

using namespace std;
#include <iostream>
#include <string>
#include <thread>
#include <mutex>

std::mutex mtx;

void oddAndEven(int n, int end);

int main()
{
std::thread odd(oddAndEven, 1, 10);
std::thread Even(oddAndEven, 2, 10);

odd.join();
Even.join();

return 0;
}



void oddAndEven(int n, int end){
int x = n;
for (; x < end;){
    mtx.lock();
    std::cout << n << " - " << x << endl;
    x += 2;
    mtx.unlock();
    std::this_thread::yield();
    continue;
 }
}

すなわち:

スレッド奇数は、開始番号1でoddAndEvenメソッドに移動するため、彼は奇数です。彼は、ロックを取得した最初の人物ですmtx.lock()

その間、スレッドEven もロックを取得しようとしますが、スレッドoddが最初にロックを取得したため、スレッドEvenは待機します。

スレッド奇数(ロックがある) に戻り、番号 1 を出力し、 でロックを解除しmtx.unlock()ます。この時点で、thread Even にロックを取得して 2 を出力させたいので、thread Evenを記述して通知しますstd::this_thread::yield()。次に、thread Even も同じことを行います。

などなど

于 2015-05-15T19:25:13.150 に答える
0

まず最初に、2 つの関数には少なくともループが含まれている必要があります (1 つの数値だけが必要な場合を除く)。

より標準的な解決策 (アイデアを再マップする) は、ミューテックス、2 つの条件変数 (奇数と偶数) と戻り値、および印刷の別の条件を含むグローバル構造を持つことです。同期を処理するために uique_lock を使用するよりも。

擬似コード:

struct global_t
{
    mutex mtx;
    int value = {0};
    condition_variable be_odd, be_even, print_it;
    bool bye = {false};

    global_t() { be_odd.notify(); }
} global;

void odd_generator()
{
    int my_odd = 1;
    for(;;)
    {
        unique_lock lock(global.mtx);
        if(global.bye) return;
        global.be_odd.wait(lock);
        global_value = my_odd; my_odd+=2;
        global.print_it.notify();
        if(my_odd > 100) bye=true;
    } //let RAII to manage wait states and unlocking
};

void even_generator()
{ /* same as odd, with inverted roles */ }

void printer()
{
    for(;;)
    {
        unique_lock lock(global.mtx);
        if(bye) return;
        global.ptint_it.wait(lock);
        std::cout << global.value << std::endl;
        ((global.value & 1)? global.be_even: global.be_odd).notify();
    }
}


int main()
{
    thread oddt(odd_generator), event(even_generator), printt(printer);
    oddt.join(), event.join(), printer.join();
}

教訓的な目的は別として、このソリューションはカウンターの値を出力する単純なループに関して何の価値も追加しないことに注意してください。

また、(グローバルを回避するために) すべてをクラスにラップし (実際のメインをクラス メソッドにする)、新しいメイン内のスタックでそのクラスをインスタンス化できることに注意してください。

于 2013-02-01T07:43:59.373 に答える
0

シリアル動作に 3 つの個別のスレッドを使用する理由がわかりません。しかし、とにかく答えます:)

解決策の 1 つは、修正されたプロデューサー/コンシューマー パターンを使用し、プロデューサーとコンシューマーの間で優先順位を付けたキューを使用することです。キューでの並べ替え操作は、ポストされたメッセージの整数値によって異なります。コンシューマーは、キュー内の要素を調べて、それが次に期待される要素であるかどうかを確認します。そうでない場合は、スリープ/待機します。

ちょっとしたコード:

class Elt implements Comparable<Elt> {
  int value;
  Elt(value) { this.value=value; }
  int compare(Elt elt);
}

class EltQueue extends PriorityBlockingQueue<Elt> { // you shouldn't inherit colelctions, has-a is better, but to make it short
  static EltQueue getInstance(); // singleton pattern
}

class Consumer{
  Elt prevElt = new Elt(-1);
  void work()
  {
    Elt elt = EltQueue.getInstance().peek();
    if (elt.getValue() == prevElt.getValue()+1)) {
      EltQueue.getInstance().poll();
      //do work on Elt
    }
  }
}

class Producer {
  int n=0; // or 1!
  void work() {
    EltQueue.getInstance().put(new Elt(n+=2));
  }
}
于 2013-02-01T07:32:05.573 に答える