1

たとえば、複数のスレッドによって同時に計算される作業があります。

デモンストレーションの目的で、作業は while ループ内で実行されます。1 回の反復で、各スレッドは作業の独自の部分を実行し、次の反復が開始される前にカウンターを 1 回インクリメントする必要があります。

私の問題は、カウンターが各スレッドによって更新されることです。

これは比較的単純なことのように思えるので、「ベストプラクティス」または一般的な方法があると思いますか?

問題を説明し、議論を助けるためのサンプルコードを次に示します。(ブーストスレッドを使用しています)

class someTask {
public:
    int mCounter; //initialized to 0
    int mTotal; //initialized to i.e. 100000
    boost::mutex cntmutex;                
    int getCount()
    {
            boost::mutex::scoped_lock lock( cntmutex );
            return mCount;
    }
    void process( int thread_id, int numThreads )
    {
        while ( getCount() < mTotal )
        {
            // The main task is performed here and is divided 
            // into sub-tasks based on the thread_id and numThreads

                            // Wait for all thread to get to this point

            cntmutex.lock();
            mCounter++;  // < ---- how to ensure this is only updated once?
            cntmutex.unlock();
        }
    }
};
4

4 に答える 4

2

カウンターをミューテックスで保護し、2 つのスレッドが同時にカウンターにアクセスできないようにしました。他のオプションは、Boost::atomicc++ 11 アトミック操作、またはプラットフォーム固有のアトミック操作を使用することです。

ただし、コードmCounterはミューテックスを保持せずにアクセスしているようです:

    while ( mCounter < mTotal )

それは問題だ。共有状態にアクセスするには、ミューテックスを保持する必要があります。

次のイディオムを使用することをお勧めします。

  1. ロックを取得します。

  2. テストやその他のことを行って、作業を行う必要があるかどうかを判断します。

  3. 決定した作業を反映するように会計を調整します。

  4. ロックを解除します。仕事する。ロックを取得します。

  5. 私たちが行った作業を反映するように会計を調整します。

  6. 完全に終了しない限り、ステップ 2 に戻ります。

  7. ロックを解除します。

于 2012-05-05T18:49:25.950 に答える
1

私は、仕事をするために複数のロック取得を行うことについて、Davidに同意しません。

Mutexesは高価であり、より多くのスレッドが競合するため、mutex基本的にシステムコールにフォールバックします。これにより、ユーザースペースからカーネルスペースへのコンテキストスイッチが発生し、呼び出し元のスレッドがスリープ状態になります。したがって、多くのオーバーヘッドが発生します。

したがって、マルチプロセッサシステムを使用している場合は、代わりにスピンロックを使用することを強くお勧めします[1]。

だから私がすることは:

=>スコープ付きロックの取得を削除して、状態を確認します。

=>上記をサポートするためにカウンターを揮発性にする

=> whileループでは、ロックを取得した後、条件チェックを再度実行します。

class someTask {
 public:
 volatile int mCounter; //initialized to 0       : Make your counter Volatile
 int mTotal; //initialized to i.e. 100000
 boost::mutex cntmutex;                

 void process( int thread_id, int numThreads )
 {
    while ( mCounter < mTotal ) //compare without acquiring lock
    {
        // The main task is performed here and is divided 
        // into sub-tasks based on the thread_id and numThreads

        cntmutex.lock();
        //Now compare again to make sure that the condition still holds
        //This would save all those acquisitions and lock release we did just to 
        //check whther the condition was true.
        if(mCounter < mTotal)
        {
             mCounter++;  
        }

        cntmutex.unlock();
    }
 }
};

[1] http://www.alexonlinux.com/pthread-mutex-vs-pthread-spinlock

于 2012-05-08T00:06:39.567 に答える
1

メッセージ パッシング ソリューションを使用する必要があります。これは、TBB や PPL などのライブラリによってより簡単に有効になります。PPL は Visual Studio 2010 以降に無料で含まれており、TBB は Intel の FOSS ライセンスの下で無料でダウンロードできます。

concurrent_queue<unsigned int> done;
std::vector<Work> work; 
// fill work here
parallel_for(0, work.size(), [&](unsigned int i) {
    processWorkItem(work[i]);
    done.push(i);
});

これはロックレスであり、外部スレッドでdone変数を監視して、完了した量と内容を確認できます。

于 2012-05-06T15:53:36.617 に答える