反発ロック条件変数信号量まとめ


マルチスレッドまたはマルチプロセスプログラミングでは、同期メカニズムが必要です.有名な信号量はプロセス間の同期を実現することができ、スレッド間の同期メカニズムの選択が比較的多く、よく使われるのは3種類あり、反発ロック、条件変数、無名信号量である.さらに、c++は、いくつかの原子操作の変数atomicを提供し、スレッド間の同期を支援することもできる.反発ロックと条件変数に対して、linuxシステムはpthread mutexとpthread conditionメカニズム(POSIX Thread)を提供し、c++標準ライブラリもstdのmutexとconditionクラスを提供し、もちろん、標準ライブラリの下位実装もシステムが提供するシステム呼び出しに基づいている.使用するときはできるだけstdが提供するクラスを使用し、インタフェースは簡単です.以下、以下のようにまとめる:一.反発ロック:mutex 1.linuxシステムはa.静的割り当てのmutexを呼び出す
#include
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;//    ,    destroy。
//both return 0 success, or a positive error number on error
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//linux             ,      。
/*   mutex   lock,    ,   EBUSY  */
int pthread_mutex_trylock(pthread_mutex_t *mutex)
#include 
/*    mutex lock, mutex        ,              ,   ETIMEDOUT  */
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
           const struct timespec *restrict_abstime)

b.動的に割り当てられたmutexは、スタックに動的に割り当てられたmutex、またはスタックに割り当てられた自動変数、または静的に割り当てられ、デフォルトの属性を使用しないmutexに対して、次の初期化インタフェースを使用する必要があります.
#include 
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_mutexattre_t *attr);
/*init   mutex      destroy。destroy   mutex,     init  */
int pthread_mutex_destroy(pthread_mutex_t *mutex);

c.mutexの属性I:PTHREAD_MUTEX_NORMAL//デフォルトII:PTHREAD_MUTEX_ERRORCHECK error checkタイプは3つの状況を検出し、この3つのタイプに遭遇し、エラーを返します.このロックは比較的遅く、一般的にデバッグに使用され、どこのロックに問題があるのかを見つけることができ、製品では推奨されていません.3つのケースは、同じスレッドが同じmutexに2回ロックされ、スレッドペアが自分のすべてのmutexにロックされず、スレッドペアがまだロックされていないmutexにロックされていない場合です.III : PTHREAD_MUTEX_RECURSIVE同じスレッドは複数回lockでき、lock内部カウントごとに+1になります.unlockの場合、ロックを解除するには同じ回数が必要です.mutexプロパティのインスタンスを設定するには、次の手順に従います.
pthread_mutex_t mtx;
pthread_mutexattr_t mtx_attr;
int s, type;
s = pthread_mutexattr_init(&mtx_attr);
if (s != 0) {
  ERROR("pthread mutexattr init error");
}
s = pthread_mutexattr_settype(&mtx_attr, PTHREAD_MUTEX_ERRORCHECK);
if (s != 0) {
  ERROR("pthread mutexattr settype error.");
s = pthread_mutex_init(&mtx, &mtx_attr);
if (s != 0)
  ERROR("pthread mutex init error.");
s = pthread_mutexattr_destroy(&mtx_attr);
if (s != 0)
  ERROR("pthread mutex attr destroy error");
/*do some thing*/
s = pthread_mutex_destroy(&mtx);
if (s != 0)
  ERROR("pthread mutex destroy error");

2.std標準ライブラリが提供するクラス(c++標準ライブラリP 989参照)c++標準ライブラリには、次のようなmutex classが用意されています.#include class std::mutex class std::recursive_mutex同じスレッドを複数回ロックできます.ロックを解除するには、最後のunlockの後に他のスレッドがロックを取得する必要があります.class std::timed_mutex//さらに、どのくらいの時間帯または時点を渡すかを定義できます.内はlockをキャプチャしようとします.このため、2つのインタフェースtry_が追加されました.lock_for(duration)とtry_lock_until(tp) class std::recursive_timed_mutex操作:mutex m;m.~mutex()/mutex lock()try_を破棄ロック()/ロック成功true unlock()try_lock_for(duration)/時間帯dur内でロックを試み、ロックに成功したらtrue try_を返すlock_until(tp)/時刻tpの前にロックしてみます.ロックに成功したらtrueに戻ります.同時に、標準ライブラリにはclass std::lock_guardテンプレートクラス注目すべきは、lock_guardオブジェクトはMutexオブジェクトのライフサイクルを管理する責任を負いません.lock_guardオブジェクトはMutexオブジェクトのロックとロック解除操作を簡略化しただけで、スレッドが反発量にロックするのを便利にし、あるlock_guardオブジェクトの宣言サイクルでは、管理されているロックオブジェクトは常にロック状態を維持します.そしてlock_guardのライフサイクルが終了すると、管理するロックオブジェクトがロック解除されます.
std::mutex val_mutex;
std::lock_guard::mutex> lg(val_mutex);//lock and automatically unlock.
/*             ##     http://blog.csdn.net/hellokandy/article/details/50592971)*/
#define AUTO_MEM_LOCK(mutex) \
  std::lock_guard __mutex_lock__##mutexlock(mutex)

try_lock()の例:
std::mutex m;
while(!m.try_lock()) {
  do_some_other_thing();
}
//adopt_lock         mutex   lock_guard,  lock   。
std::lock_guard<:mutex> lg(m,std::adopt_lock);

ロックを複数ロック
std::mutex m1;
std::mutex m2;
{
  std::lock(m1,m2);//lock both mutexes.    mutex      
  std::lock_guard::mutex> lockm1(m1, std::adopt_lock);
  std::lock_guard::mutex> lockm2(m2, std::adopt_lock);
}

unique_lock:c++標準ライブラリにはclass uniqueも用意されています.lock<>テンプレートクラス.とlock_guardの機能は非常に似ているが,解析時にmutexがロックされ,その解析関数が自動的にロックされ,ロックされていなければ何もしないという独自の利点がある.比lock_guardは3つの構造関数を追加しました:std::try_to_lock std::chrono::seconds(1) std::defer_lock.一般的にunique_をお勧めしますlock、機能は比較的に完備しています.
//std::try_to_lock       mutex,       。
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if(lock)//if lock was successful,     mutex     
  do_some_thing()
//                ,                 (           )
std::unique_lock<std::mutex> lock(mutex, std::chrono::seconds(1));//1s   
std::unique_lock<std::mutex> lock(mutex, std::chrono::milliseconds(1));//1ms   
// std::defer_lock       lock object,        。
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
do_some_thing();
lock.lock();//    
do_some_thing()
lock.unlock();//  
//unique_lock         unlock。  ,    release()      mutex   mutex         lock。

二.条件変数:condition:条件変数conditionは、1つのスレッドが共有変数の状態変化について他のスレッドに通知し、他のスレッドが共有変数が変化していない間に待機することを許可します.conditionはmutexと協力して使用しなければならない.条件変数はステータス情報を保存せず、signal時にスレッドが待機していないとsignalが失われ、signalの後にスレッドwaitがある場合は、次のsignalが起動するまで待つ必要があります.1.linuxが提供するシステム呼び出しa.静的割り当ての条件変数pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
#include 
/*All return 0 on success, or a positive error number on error*/
int pthread_cond_signal(pthread_cond_t *cond);//         wait
int pthread_cond_broadcast(pthread_cond_t *cond); //       wait
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)//       signal or broadcast。
/*         ,   ETIMEOUT  */
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

b.動的に割り当てられた条件変数は、スタックに動的に割り当てられたcondition、またはスタックに割り当てられた自動変数、または静的に割り当てられ、デフォルト属性を使用しないconditionに対して、次の初期化インタフェースを使用する必要があります.
#include
/*return 0 on success, or a positive error number on error*/
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);//     condition   destroy。

次に、生産者-消費者の例を示します.
//   
s = pthread_mutex_lock(&mtx);
if (s != 0) 
  ERROR("pthread mutex lock error");
avail ++;
s = pthread_mutex_unlock(&mtx);
if (s != 0) 
  ERROR("pthread mutex unlock error");
s = pthread_cond_signal(&cond);
if (s != 0) 
  ERROR("pthread cond signal error");
//   
for(;;)
{
  s = pthread_mutex_lock(&mtx);
  if (s != 0) 
    ERROR("pthread mutex lock error");
  while(avail == 0) {
    s = pthread_cond_wait(&cond, &mtx);//      unlock,      lock。
    if (s != 0) 
      ERROR("pthread cond wait error");
  }
  while(avail > 0) {
    /*do something*/
    avail --;
  }
  s = pthread_mutex_unlock(&mtx);
  if (s != 0) 
   ERROR("pthread mutex unlock error");
}

2.std標準ライブラリが提供するクラス
#include 
#include  //  condition         mutex
std::mutex ready_mutex;
std::condition_variable ready_cond;
//       , mutex    。
read_cond.notify_one();//notify one of the waiting threads
read_cond.notify_all();//notify all the waiting threads
//     
std::unique_lock<std::mutex> lk(ready_mtuex);//    lock_guard.   lock        。
while(!ready_flag) {
  read_cond.wait(lk);// wait      ,                        。  wait        while  。
}
     lambda           
std::unique_lock<std::mutex> lk(ready_mutex);
ready_cond.wait(lk, []{return ready_flag;});//        ready_flag,    true     。

conditionのインタフェース:
conditon_variable cv;
cv.notify_one();
cv.notify_all();
cv.wait(lk);
cv.wait(lk, pred)//    pred   true     。
cv.wait_for(lk, duration);//     std::cv_status::timeout std::cv_status_no_timeout
cv.wait_for(lk, duration, pred);//    wait
cv.wait_until(lk, timepoint);
cv.wait_until(lk, timepoint,pred);
notify_all_at_thread_exit(cv,lk);//    exit notify   wait。

三.atomic変数atomicは、boolに値を付与しても原子であることが保証されないため、atomicテンプレートクラスが多様な変数を提供するatomicメカニズムを提供する比較的簡単な同期メカニズムを提供する.テンプレートクラスなのでbool,int type,ptr typeに使用できます.
#include 
std::atomic ready_flag(false);
ready_flag.store(true);//     (ready_flag = true;)
ready_flag.load();//std::atomic int_flag;
++ int_flag;
int_flag = 5;
int_flag &= 0x01;
int_flag |= 0x01;
int_flag ^= 0x01;

また、c言語を互換化するために、atomic_などのcインタフェースも提供されています.boolなど.「c++標準ライブラリ」P 1019参照四.信号量:semaphore:信号量は、一般的に、プロセス間通信および共有リソースへの同期アクセスに使用されます.linuxシステムは2つの信号量メカニズムを提供し、system v信号量とposix信号量、system v信号量は比較的複雑であり、現在はposix信号量が一般的に使用されている.標準ライブラリには信号量のメカニズムパッケージが提供されていません.信号量はまた、有名信号量(ファイルベース)と名前なし信号量(メモリベース)に分けられる.有名信号量はプロセス間の通信を実現することができ、名前なし信号量はプロセスを跨ぐことができない.2つのプロセスの共有メモリにない限り.posix信号量は整数であり、その値は0より小さくてはならない.1.名前付き信号量:
#include  //defines o_* flags
#include //defines mode 
#include 
/*     semaphore,      ,    SEM_FAILED。 oflag      flag  ,   0,          ,     O_CREAT,      name         ,          。       O_CREAT O_EXCL,      name        ,     。mode      */
sem_t *sem_open(const char *name, int oflag, .../*mode_t mode, unsigned int value*/);
/*return 0 on success, or -1 on error*/
int sem_close(sem_t *sem);//     
int sem_unlink(const char *name);//     。return 0 on success, or -1 on error.
/*wait   ( 1)sem        。       0,     ,    0,  wait。          ,      ,error_number EINTR.*/
int sem_wait(sem_t *sem);
/*sem_wait      ,     value   0,    ,   EAGAIN  。*/
int sem_try_wait(sem_t *sem);//return 0 on success, or -1 on error.
/*    ETIMEDOUT  ,       。*/
#define _XOPEN_SOURCE 600
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//return 0 on success, or -1 on error.
/*       (  1)*/
int sem_post(sem_t *sem);//return 0 on success, or -1 on error.
/*         */
int set_getvalue(sem_t *sem, int *sval);

2.未命名信号量:未命名信号量はメモリベース信号量とも呼ばれる.そのsem_post, sem_wait, sem_getvalueなどの関数は有名な信号量と同じですが、信号量を初期化および破棄するために追加の2つの関数が提供されています.
#include 
/*  value       sem              。pshared   0,       ,     0,       ,  , sem              。*/
int sem_init(sem_t *sem, int pshared, unsigned int value);//return 0 on success, or -1 on error.
sem_t sem;
sem_init(&sem, 0, 0);
int sem_destroy(sem_t *sem);//return 0 on success, or -1 on error.