0

このシナリオは常に頻繁に発生します。いくつかのスレッドと共有オブジェクトがあり、常に 1 つのスレッドだけがそのオブジェクトを変更できるようにする必要があります。

まあ、明らかな解決策はlock the door-do the job-get out of thereイディオムを使うことです。この状況では、私は常に POSIX ミューテックスを使用します。例えば

pthread_mutex_lock(&this->messageRW);     // lock the door
P_Message x = this->messageQueue.front(); // do the job
this->messageQueue.pop(); 
pthread_mutex_unlock(&this->messageRW);   // get out of there
// somewhere else, in another thread
while (true) {
    P_Message message;
    solver->listener->recvMessage(message);
    pthread_mutex_lock(&(solver->messageRW));     // lock the door
    solver->messageQueue.push(message);           // do the job
    pthread_mutex_unlock(&(solver->messageRW));   // get out of there
    sem_post(&solver->messageCount);
}

messageQueueはコードの非常に多くの場所で使用しています。そのため、洗練されていないロック/ロック解除のペアがたくさんありました。messageQueueスレッド間で共有されるはずのオブジェクトとして宣言する方法が必要だと思います。そうすれば、スレッド API がロック/ロック解除を処理できます。ラッパー クラスなどを考えることができます。POSIX ベースのソリューションが推奨されますが、他の API (ブースト スレッドや他のライブラリ) も使用できます。

同様の状況で何を実装しますか?

将来の読者のための更新

私はこれを見つけまし。C++14 の一部になると思います。

4

3 に答える 3

2

こんな場合に使えますboost:scoped_lock。範囲外に出るとすぐに、エレガントにロックが解除されます:

 boost::mutex mMutex;//member mutex object defined somewhere


{ //scope operator start
   boost::mutex::scoped_lock scopedLock(mMutex);
   pthread_mutex_lock();     // scoped lock the door
   P_Message x = this->messageQueue.front(); // do the job
   this->messageQueue.pop(); 
} //scope operator end, unlock mutex

// somewhere else, in another thread
while (true) {
    P_Message message;
    solver->listener->recvMessage(message);
    boost::mutex::scoped_lock scopedLock(mMutex);     // scoped lock the door
    solver->messageQueue.push(message);           // do the job
    sem_post(&solver->messageCount);
} //scope operator end, unlock mutex
于 2013-07-03T08:11:25.970 に答える
2

このために、ミューテックスの使用を強制する別のクラスにメッセージ キュー クラスをサブクラス化 (is-a) するか、インクルード (has-a) します。

synchronized機能的には、Javaキーワードなどを使用して他の言語が行うことです。これは、基になるオブジェクトを自動的に保護されるように変更します。

于 2013-07-03T08:12:08.487 に答える
1

ロックを処理する (アトミックである) のはメッセージ キュー自体であり、呼び出し元のコードではありません。また、ミューテックスだけでなく、競合状態を回避するための条件も必要です。標準的なイディオムは次のようになります。

class ScopedLock    //  You should already have this one anyway
{
    pthread_mutex_t& myOwned;
    ScopedLock( ScopedLock const& );
    ScopedLock& operator=( ScopedLock const& );
public:
    ScopedLock( pthread_mutex_t& owned )
        : myOwned( owned )
    {
        pthread_mutex_lock( &myOwned );
    }
    ~ScopedLock()
    {
        pthread_mutex_unlock( &myOwned );
    }
};

class MessageQueue
{
    std::deque<Message> myQueue;
    pthread_mutex_t myMutex;
    pthread_cond_t myCond;
public:
    MessageQueue()
    {
        pthread_mutex_init( &myMutex );
        pthread_cond_init( &myCond );
    }
    void push( Message const& message )
    {
        ScopedLock( myMutex );
        myQueue.push_back( message );
        pthread_cond_broadcast( &myCond );
    }

    Message pop()
    {
        ScopedLock( myMutex );
        while ( myQueue.empty() ) {
            pthread_cond_wait( &myCond, &myMutex );
        }
        Message results = myQueue.front();
        myQueue.pop_front();
        return results;
    }
};

これにはさらにエラー処理が必要ですが、基本的な構造はそこにあります。

もちろん、C++11 を使用できる場合は、標準のスレッド プリミティブを使用した方がよいでしょう。(それ以外の場合は、Boost スレッドをお勧めします。ただし、既に Posix スレッドを使用している場合は、2 回変換するのではなく、標準スレッドを使用できるようになるまで変換を待つことをお勧めします。) ただし、両方のミューテックスが必要になります。そして条件。

于 2013-07-03T08:27:39.563 に答える