C++パッケージ反発量と条件変数

4672 ワード

はんぱつりょう
(1)反発量は臨界領域を保護する別の方法であり、実行スレッドが臨界領域での実行時間が長い場合、反発量を使用することが望ましい.そうしないと、他のスレッドが臨界領域外で忙しくなるなど、CPU時間を浪費する.この時、他のスレッドは臨界領域が反発量によってロックされていることを発見し、それらはブロックされる.反発量が解放されると、複数の線があるスレッドがブロックされている場合、複数のスレッドが起動しますが、ロックを取得できるのは1つのスレッドだけで、他のスレッドはブロックを続けます.
(2)実行スレッドが臨界領域で睡眠をとる必要がある場合は,反発量を用いることが好ましく,スピンロックを用いると他のスレッドが臨界領域外で忙しくなるなどCPU時間を浪費する.
(3)Posixの反発量は再帰的ロックと非再帰的ロックをサポートしており,非再帰的ロックではデッドロックが発生する可能性があるので,ある反発量を持つスレッドがそのロックを保持し続ける場合,再帰がサポートされていないためプログラムはデッドロックになると考えられる.(自分で自分をロックする)、さらにプログラムの論理を修正する必要があるかもしれません.再帰的なロックは、スレッドを実行し続けることができますが、臨界領域のデータが破壊され、プログラムがクラッシュする可能性があります.
反発量の実現
(C++パッケージLinuxのpthreadシステム呼び出し)
class Mutex final  
{  
public:  
  Mutex(const Mutex&) = delete;  
  Mutex& operator=(const Mutex&) = delete;  
  
  explicit Mutex(bool processShared = false) :  
    _processShared(processShared)  
  {  
    pthread_mutexattr_t attr;  
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);  
  
    if (processShared) {  
        int shared;  
        pthread_mutexattr_getpshared(&attr, &shared);  
        assert(shared ==  PTHREAD_PROCESS_PRIVATE);  
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);  
    }  
     
    int err = pthread_mutex_init(&_mutex, &attr);  
    (void) err;  
  }  
  
  ~Mutex()  
  {  
    int err = pthread_mutex_destroy(&_mutex);  
    (void) err;  
  }  
  
  int lock()  
  {  
    return pthread_mutex_lock(&_mutex);  
  }  
  
  int unlock()  
  {  
    return pthread_mutex_unlock(&_mutex);  
  }  
  
  pthread_mutex_t* getMutexPtr()  
  {  
    return &_mutex;  
  }  
  
private:  
  pthread_mutex_t _mutex;  
  bool _processShared;  
};  

いくつかの点を説明します.
(1)_processSharedパラメータは、プロセス間の反発量をサポートするかどうかであり、デフォルトでは単一プロセスのfalseである.反発量の属性はPTHREAD_MUTEX_NORMALであり、再帰的なロックは許可されない.
(2)pthread_mutex_t*getMutexPtr()は条件変数のために実現されており、以下に説明する.
(3)C++における構造関数と解析関数を用いて,反発量を初期化し破壊する.
反発量の使用
class MutexLockGuard final  
{  
public:  
  MutexLockGuard(const MutexLockGuard&) = delete;  
  MutexLockGuard& operator=(const MutexLockGuard&) = delete;  
  
  explicit MutexLockGuard(Mutex& mutex) :  
      _mutex(mutex)  
  {  
    _mutex.lock();  
  }  
  
  ~MutexLockGuard()  
  {  
    _mutex.unlock();  
  }  
  
private:  
  Mutex& _mutex;  
};  

説明:
MutexLockGuardで保有しているmutex;C++の構造関数と構造関数を利用して、反発量を申請し、解放する.
じょうけんへんすう
(1)臨界領域において、ある条件が成立するのを待つ必要がある場合、条件変数を使用するべきである.この_cond.wait()プロセスは、呼び出しスレッドを待機条件のスレッドリストに配置し、反発量をロック解除する.このとき、反発量のロック解除中に、新しいスレッドがこの臨界領域に入り、条件はまだ発生していない.cond.wait()はこのプロセスを継続します.
(2)コードセグメント2では、まず条件チェック(同じ反発量でロックされているので、睡眠中のスレッドは見逃すことはできない)が行われ、_count=0_cond.wakeAll()がスレッドを起動し、条件が変化した後にスレッドを起動する必要があることを覚えておく.
(3)まず_cond.wait()は_mutexがロックされている場合に呼び出すことができます.cond.wait()はロックを解除する過程に関連する.
(4)if(_count>0)ではなくwhile(_count>0)を使用する必要があります.なぜなら、スレッドが_からcond.wait()が起動すると、このとき反発量がロックされ続け(このとき複数のスレッドが反発量を競合する問題)、この場合の条件が他のスレッドに修正され、_count>0の条件が成立しない可能性が高いため、判断を継続する必要がある.
(5)_cond.wakeAll()を複数回実行して信号を送信する場合、スレッドが待機条件リストにブロックされていない場合、この信号は失われるが、プログラムに影響を与えない.
コードクリップ1:
MutexLockGuard lock(_mutex);  
while (_count > 0)    //impotant  
  {  
    _cond.wait();  
  }  

コードクリップ2:
{  
  MutexLockGuard lock(_mutex);  
  --_count;  
}   
if (_count == 0)  
  {  
    _cond.wakeAll();  
  }  

条件変数の実装:
class Condition final  
{  
public:  
  Condition(const Condition&) = delete;  
  Condition& operator=(const Condition&) = delete;  
  
  Condition(Mutex& mutex) :  
      _mutex(mutex)  
  {  
    int err = pthread_cond_init(&_cond, NULL);  
    (void) err;  
  }  
  
  ~Condition()  
  {  
    int err = pthread_cond_destroy(&_cond);  
    (void) err;  
  }  
  int wait()  
  {  
    return pthread_cond_wait(&_cond, _mutex.getMutexPtr());;  
  }  
  int waitForSeconds(size_t seconds)  
  {  
    struct timespec ts;  
    clock_gettime(CLOCK_REALTIME, &ts);  
  
    ts.tv_sec += seconds;  
    return pthread_cond_timedwait(&_cond, _mutex.getMutexPtr(), &ts);  
  }  
  int wake()  
  {  
    return pthread_cond_signal(&_cond);  
  }  
  int wakeAll()  
  {  
    return pthread_cond_broadcast(&_cond);  
  }  
  
private:  
  Mutex& _mutex;  
  pthread_cond_t _cond;  
};  

いくつかの点を説明します.
(1)wakeは少なくとも1つのスレッドを起動するためであり、wakeAllはすべてのスレッドを起動するためである.waitForSeconds(size_t seconds)はseconds秒を待つために条件がまだ現れない場合、スレッドは反発量を再獲得する(この場合、複数のスレッドが反発量を競合する問題).
(2)wait()の実装には使用が必要である_mutex.getMutexPtr()のpthread_mutex_tタイプの_mutex;