APUE学習ノート——11スレッド同期、反発ロック、スピンロック、条件変数
87655 ワード
スレッド同期
同じプロセスに属する異なるスレッドは共有メモリであるため、実行中にデータの一貫性を考慮する必要があります.
仮に、プロセスに変数i=0があり、スレッドAがi++を実行し、スレッドBがi++を実行するとしたら、最終iの値はいくらですか?i=2に違いないようです.実はそうではありません.スレッド同期を考慮していない場合、iの値は1である可能性があります.まず、自己加算動作のプロセスを考慮します.aは、まずメモリ内のiの値をレジスタにcopyします.b、レジスタ中のiのcopyを自己加算する.c,レジスタに自己加算された結果をメモリに返す.例に戻ると、スレッドAがabcの3つのステップを実行し、スレッドBが実行者の3つのステップを実行した場合、結果は2であるべきである.しかし、自己加算は原子操作ではなく、実行過程がa(A)、a(B)、b(A)、b(B)、c(A)、c(B)である場合、ABはiの原値i=0 copyをレジスタに自己加算し、結果1を得た後、メモリに値1を付与iが、iの結果は1となる.
上記の望ましくない結果(i=2ではなくi=1)が現れる可能性があるのは、i++が原子操作ではないためである.この問題を解決するために、反発ロックが導入される.
反発ロックMutexes
反発ロックはpthreadsの反発インタフェースを通じて、データがある時間に1つのスレッドしかアクセスできないことを保証します.
反発ロックの使用手順は,1)共有リソースにアクセスする前にロックをかけ,2)共有リソースにアクセスする,3)アクセスが加速した後にロックを解除する.(この過程で反発量はロックの役割を果たす)
反発量使用構造体pthread_mutex_tは、使用するインタフェースが以下のように表示される.
反発量の初期化と破棄:
動的に作成された反発量mutexを使用する必要があります
pthread_mutex_destroy,
静的に反発量を作成する場合,直接mutex=
PTHREAD_MUTEX_INITIALIZERは起動せずに初期化する
pthread_mutex_init
で行ないます.
パラメータattrは反発量の属性を表す.
ロックとロック解除:
使用
pthread_mutex_lock
場合、反発量が他のスレッドにロックされている場合、ブロックされ、その反発量がロック解除され、反発量がロックされることが分かった.
使用
pthread_mutex_trylock
の場合、反発量が他のスレッドにロックされている場合
を選択すると、関数はエラーを返します.ロックされていない場合は、反発量にロックされます.
使用
pthread_mutex_timedlock
の場合は、ロックをかけてみて、ブロックしてみます(最大は時点tsptrまでブロックします).tsptrでロックができない場合は、エラーが返されます.tsptrについては、3 min待つなどの時点で、結果としてtsptrは現在の時間に3 min加算されます.
デッドロックとデッドロックの回避
デッドロックが発生した場合:
1)スレッドは同一の反発量に対して2回ロックし、以下のような状況である.
2行目にブロックが発生し、デッドロックが発生します.
2)複数の反発量があり、異なるスレッドがそれぞれ1つの反発量をロックし、他の反発量を要求するときにブロックされる.
同じプロセスに属する異なるスレッドは共有メモリであるため、実行中にデータの一貫性を考慮する必要があります.
仮に、プロセスに変数i=0があり、スレッドAがi++を実行し、スレッドBがi++を実行するとしたら、最終iの値はいくらですか?i=2に違いないようです.実はそうではありません.スレッド同期を考慮していない場合、iの値は1である可能性があります.まず、自己加算動作のプロセスを考慮します.aは、まずメモリ内のiの値をレジスタにcopyします.b、レジスタ中のiのcopyを自己加算する.c,レジスタに自己加算された結果をメモリに返す.例に戻ると、スレッドAがabcの3つのステップを実行し、スレッドBが実行者の3つのステップを実行した場合、結果は2であるべきである.しかし、自己加算は原子操作ではなく、実行過程がa(A)、a(B)、b(A)、b(B)、c(A)、c(B)である場合、ABはiの原値i=0 copyをレジスタに自己加算し、結果1を得た後、メモリに値1を付与iが、iの結果は1となる.
上記の望ましくない結果(i=2ではなくi=1)が現れる可能性があるのは、i++が原子操作ではないためである.この問題を解決するために、反発ロックが導入される.
反発ロックMutexes
反発ロックはpthreadsの反発インタフェースを通じて、データがある時間に1つのスレッドしかアクセスできないことを保証します.
反発ロックの使用手順は,1)共有リソースにアクセスする前にロックをかけ,2)共有リソースにアクセスする,3)アクセスが加速した後にロックを解除する.(この過程で反発量はロックの役割を果たす)
反発量使用構造体pthread_mutex_tは、使用するインタフェースが以下のように表示される.
反発量の初期化と破棄:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t*restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Both return: 0 if OK, error number on failure
動的に作成された反発量mutexを使用する必要があります
pthread_mutex_destroy,
静的に反発量を作成する場合,直接mutex=
PTHREAD_MUTEX_INITIALIZERは起動せずに初期化する
pthread_mutex_init
で行ないます.
パラメータattrは反発量の属性を表す.
ロックとロック解除:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
All return: 0 if OK, error number on failure
使用
pthread_mutex_lock
場合、反発量が他のスレッドにロックされている場合、ブロックされ、その反発量がロック解除され、反発量がロックされることが分かった.
使用
pthread_mutex_trylock
の場合、反発量が他のスレッドにロックされている場合
を選択すると、関数はエラーを返します.ロックされていない場合は、反発量にロックされます.
使用
pthread_mutex_timedlock
の場合は、ロックをかけてみて、ブロックしてみます(最大は時点tsptrまでブロックします).tsptrでロックができない場合は、エラーが返されます.tsptrについては、3 min待つなどの時点で、結果としてtsptrは現在の時間に3 min加算されます.
デッドロックとデッドロックの回避
デッドロックが発生した場合:
1)スレッドは同一の反発量に対して2回ロックし、以下のような状況である.
pthread_mutex_lock(mutex);
pthread_mutex_lock(mutex);
pthread_mutex_unlock(mutex);
pthread_mutex_unlock(mutex);
2行目にブロックが発生し、デッドロックが発生します.
2)複数の反発量があり、異なるスレッドがそれぞれ1つの反発量をロックし、他の反発量を要求するときにブロックされる.
//thread A
pthread_mutex_lock(mutex1);
pthread_mutex_lock(mutex2);
pthread_mutex_unlock(mutex2);
pthread_mutex_unlock(mutex1);
//thread B
pthread_mutex_lock(mutex2);
pthread_mutex_lock(mutex1);
pthread_mutex_unlock(mutex1);
pthread_mutex_unlock(mutex2);
A mutex1, mutex2 ,B mutex2. B mutex1 , A 。
, , , 。
, , 。
pthread_mutex_lock
(
mutex1
);
pthread_mutex_lock
(
mutex2
);
。
, :
struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
/* ... more stuff here ... */
};
, 。
:
1) , 。
2) ( ) ,
3) , 。
, , , , 。 。
( ):
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t*restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
Both return: 0 if OK, error number on failure
:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
All return: 0 if OK, error number on failure
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
Both return: 0 if OK, error number on failure
#include <pthread.h>
#include <time.h>
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrictrwlock,const struct timespec*restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrictrwlock,const struct timespec *restric ttsptr);
, 。
。
:pthread_cond_t 。
:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
Both return: 0 if OK, error number on failure
。 PTHREAD_COND_INITIALIZER 。
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,
const
struct
timespec
*
restrict tsptr
);
pthread_cond_wait
。
1) mutex
2) ,
3) mutex。
pthread_cond_timedwait
tsptr
, 3min , tsptr 3min
:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
Both return: 0 if OK, error number on failure
Example:
#include <pthread.h>
struct msg {
struct msg *m_next;
/* ... more stuff here ... */
};
struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void
process_msg(void)
{
struct msg *mp;
for (;;) {
pthread_mutex_lock(&qlock);
while (workq == NULL)
pthread_cond_wait(&qready, &qlock);
mp = workq;
workq = mp->m_next;
pthread_mutex_unlock(&qlock);
/* now process the message mp */
}
}
void
enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&qready);
}
Spin lock
。 。 , , 。 , , , , 。
。 。 。
:
#include <pthread.h>
int pthread_spin_init(pthread_spinlock_t *lock,intpshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
Both return: 0 if OK, error number on failure
#include <pthread.h>
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
All return: 0 if OK, error number on failure
Barriers
pthread_barrier_
, , , 。
:
#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrictbarrier,const pthread_barrierattr_t *restrict attr,unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
Both return: 0 if OK, error number on failure
count 。
#include <pthread.h>
int pthread_barrier_wait(pthread_barrier_t *barrier);
Returns: 0 orPTHREAD_BARRIER_SERIAL_THREADif OK, error number on failure
pthread_barrier_wait , 。
,8 (count=0),
pthread_barrier_wait
, ( ) , 8 ,
pthread_barrier_wait
。