2016-08-24 7 views
1

セクションをパラレルで実行した後、メインスレッドにマージしてから、セクションをパラレルに戻したいとします。子供時代のゲームの赤い光の緑色のライトに似ています。C++フォーク結合並列処理ブロッキング

私は何をしようとしているのかの例を挙げました。条件変数を使ってスレッドをブロックしますが、それらをすべて並列に開始したいが、最後にブロックするそれらは連続してプリントアウトすることができます。 * =操作は、数秒に及ぶはるかに大きな操作になる可能性があります。スレッドを再利用することも重要です。タスクキューを使用するのは重すぎるかもしれません。

私は多忙なループでこの問題を解決する方法を知っているので、単純なビジーループではない何らかのブロッキング構造を使用する必要があります。英語で

  1. スレッド1は、
  2. スレッド(お互いをブロックせずに)すべてのスレッドを起動する1信号
  3. スレッド2-11工程その専用記憶
  4. をブロックされている10個のスレッドを作成しますスレッド1は2-11が完了するまで待っています(ここでカウントするにはアトミックを使用できます)
  5. スレッド2-11が完了し、必要に応じて状態を確認するためにそれぞれ1に通知することができます
  6. スレッド1つのチェックの条件と2

例コード(ナイーブがcplusplus.com上の例から採用)から連続配列

  • スレッド1 resignals 2-11再び処理するために、印刷:

    // condition_variable example 
    #include <iostream>   // std::cout 
    #include <thread>    // std::thread 
    #include <mutex>    // std::mutex, std::unique_lock 
    #include <condition_variable> // std::condition_variable 
    #include <atomic> 
    
    std::mutex mtx; 
    std::condition_variable cv; 
    bool ready = false; 
    std::atomic<int> count(0); 
    
    bool end = false; 
    int a[10]; 
    
    void doublea (int id) { 
        while(!end) { 
        std::unique_lock<std::mutex> lck(mtx); 
        while (!ready) cv.wait(lck); 
        a[id] *= 2; 
        count.fetch_add(1); 
        } 
    } 
    
    void go() { 
        std::unique_lock<std::mutex> lck(mtx); 
    
        ready = true; 
        cv.notify_all(); 
        ready = false; // Naive 
    
        while (count.load() < 10) sleep(1); 
        for(int i = 0; i < 10; i++) { 
        std::cout << a[i] << std::endl; 
        } 
    
        ready = true; 
        cv.notify_all(); 
        ready = false; 
        while (count.load() < 10) sleep(1); 
        for(int i = 0; i < 10; i++) { 
        std::cout << a[i] << std::endl; 
        } 
    
        end = true; 
        cv.notify_all(); 
    } 
    
    int main() { 
        std::thread threads[10]; 
        // spawn 10 threads: 
        for (int i=0; i<10; ++i) { 
        a[i] = 0; 
        threads[i] = std::thread(doublea,i); 
        } 
    
        std::cout << "10 threads ready to race...\n"; 
        go();      // go! 
    
        return 0; 
    } 
    
  • +0

    なぜ「カウント」アトミックですか?なぜ一方の側で条件変数を使用し、別の側でビジーなスリープを使用するのですか? – UmNyobe

    +0

    @UmNyobe私は明らかにビジーな睡眠を使いたくありません。スレッドは通知を待つ間にスリープ状態になるはずです。 あなたの疑問にお答えしますと、カウントは、一度に複数のスレッドで増加する可能性があるため、アトミックです。増分は2段階のプロセスです。 –

    答えて

    1

    効率的に実装するのは簡単ではありません。さらに、あなたがこの主題を学ばない限り、それは意味をなさない。条件変数は、うまく拡張できないため、ここでは適切な選択肢ではありません。

    成熟したランタイムライブラリがfork-joinの並列処理をどのように実装しているかを見て、それらから学習したり、アプリケーションで使用することをお勧めします。 http://www.openmprtl.org/http://opentbb.org/https://www.cilkplus.org/ - これらはすべてオープンソースです。

    OpenMPは、探しているものに最も近いモデルであり、フォーク結合障壁の最も効率的な実装をしています。しかし、それはHPCのために設計されており、動的な構成可能性が欠けているので、その欠点があります。 TBBとCilkは、外部並列領域のコンテキストで使用できるモジュールとライブラリのネストされた並列処理と使用に最適です。

    +0

    あなたは正しいことを学んでいると言っています。私は以前にTBBとOpenMPの両方を見てきましたが、OpenMPのプラグマの大量使用はオフになっており、TBBには単純なことをするためのボイラープレートがたくさんあります。 しかし、私は彼らに別の表情を与えて喜んで使用します。実際に私の質問を読むために、私はあなたに答えを与えるでしょう。 –

    +0

    フロントエンド(使用モデル)とランタイムライブラリを混在させたくありません。これらのランタイムには、独自のフロントエンドを作成できます。 – Anton

    0

    バリアまたは条件変数を使用すると、すべてのスレッドを開始できます。スレッド1は、すべてのスレッドがすべてのスレッドを終了するまで待つことができます(すべてのスレッドの結合メソッドによってブロックされています)。

    +0

    これは、一度結合するとスレッドを再作成する必要があるため、解決策ではありません。 –