18

プロジェクトからすべてのブースト参照を取り除き、純粋な C++ 11 に切り替えようとしています。

ある時点で、バリアが「go」コマンドを発行するのを待って作業を行い (N 個のスレッドに分散)、すべての作業が終了したときに同期するスレッド ワーカーが作成されます。基本的な考え方は、メインループが go order (boost::barrier .wait()) を与え、同じ関数で結果を待つというものです。

別のプロジェクトで、Boost バージョンに基づいてカスタム メイドの Barrier を実装しましたが、すべてが完全に機能しました。実装は次のとおりです。

Barrier.h:

class Barrier {
public:
    Barrier(unsigned int n);
    void Wait(void);
private:
    std::mutex counterMutex;
    std::mutex waitMutex;

    unsigned int expectedN;
    unsigned int currentN;
};

バリア.cpp

Barrier::Barrier(unsigned int n) {
    expectedN = n;
    currentN = expectedN;
}

void Barrier::Wait(void) {
    counterMutex.lock();

    // If we're the first thread, we want an extra lock at our disposal

    if (currentN == expectedN) {
        waitMutex.lock();
    }

    // Decrease thread counter

    --currentN;

    if (currentN == 0) {
        currentN = expectedN;
        waitMutex.unlock();

        currentN = expectedN;
        counterMutex.unlock();
    } else {
        counterMutex.unlock();

        waitMutex.lock();
        waitMutex.unlock();
    }
}

このコードは iOS と Android の NDK で問題なく使用されていますが、Visual Studio 2013 プロジェクトで試してみると、ミューテックスをロックしたスレッドのみがロックを解除できるようです (アサーション: 所有されていないミューテックスのロック解除)。

C ++ 11で機能する、回転しない(このようなブロッキング)バージョンのバリアを使用できますか? ビジー待機を使用するバリアを見つけることができましたが、これは防止したいものです (本当に理由がない場合を除きます)。

4

3 に答える 3

33
class Barrier {
public:
    explicit Barrier(std::size_t iCount) : 
      mThreshold(iCount), 
      mCount(iCount), 
      mGeneration(0) {
    }

    void Wait() {
        std::unique_lock<std::mutex> lLock{mMutex};
        auto lGen = mGeneration;
        if (!--mCount) {
            mGeneration++;
            mCount = mThreshold;
            mCond.notify_all();
        } else {
            mCond.wait(lLock, [this, lGen] { return lGen != mGeneration; });
        }
    }

private:
    std::mutex mMutex;
    std::condition_variable mCond;
    std::size_t mThreshold;
    std::size_t mCount;
    std::size_t mGeneration;
};
于 2014-11-25T03:51:33.047 に答える
3

これは、繰り返し使用するための自動リセット動作を備えた、上記の受け入れられた回答の私のバージョンです。これは、カウントアップとカウントダウンを交互に行うことで達成されました。

    /**
    * @brief Represents a CPU thread barrier
    * @note The barrier automatically resets after all threads are synced
    */
    class Barrier
    {
    private:
        std::mutex m_mutex;
        std::condition_variable m_cv;

        size_t m_count;
        const size_t m_initial;

        enum State : unsigned char {
            Up, Down
        };
        State m_state;

    public:
        explicit Barrier(std::size_t count) : m_count{ count }, m_initial{ count }, m_state{ State::Down } { }

        /// Blocks until all N threads reach here
        void Sync()
        {
            std::unique_lock<std::mutex> lock{ m_mutex };

            if (m_state == State::Down)
            {
                // Counting down the number of syncing threads
                if (--m_count == 0) {
                    m_state = State::Up;
                    m_cv.notify_all();
                }
                else {
                    m_cv.wait(lock, [this] { return m_state == State::Up; });
                }
            }

            else // (m_state == State::Up)
            {
                // Counting back up for Auto reset
                if (++m_count == m_initial) {
                    m_state = State::Down;
                    m_cv.notify_all();
                }
                else {
                    m_cv.wait(lock, [this] { return m_state == State::Down; });
                }
            }
        }
    };  
于 2015-06-01T05:46:24.667 に答える