http://blog.csdn.net/zg_hover/article/details/3929833
2009
同 步
在進(jìn)行多線程和多進(jìn)程編程的時候,總會遇到多個進(jìn)程或多個線程對同一塊數(shù)據(jù)的訪問。這是我們就需要使用某種同步的手段來保證數(shù)據(jù)的正確訪問。
1 互斥鎖和條件變量
使用范圍:同一進(jìn)程中的不同線程間;
1.1 互斥鎖
*基本概念
互斥鎖是指相互排斥,是最基本的同步形式。它可以用來保護(hù)臨界區(qū),以保證任何時候都只有多個線程中的一個線程在期間運(yùn)行。
注意:如果在多個進(jìn)程間,而且每個進(jìn)程都有獨(dú)立的互斥鎖變量的內(nèi)存空間,這樣每個進(jìn)程都可以調(diào)用pthread_mutex_lock那么這樣的互斥鎖將失去意義。例如:
下面的代碼是usp上的14-1的一段
...
pthread_mutex_t mutex;
...
for (i = 1; i < n; i++)
if (childpid = fork()) //創(chuàng)建進(jìn)程鏈
break;
snprintf(buffer, BUFSIZE,
"i:%d process ID:%ld parent ID:%ld child ID:%ld/n",
i, (long)getpid(), (long)getppid(), (long)childpid);
c = buffer;
/********** start of critical section *****************/
//到這里每個子進(jìn)程都擁有mutex的內(nèi)存空間和變量值,所以這里無法完成互斥
pthread_mutex_lock(&mutex);
while (*c != '/0') {
fputc(*c, stderr);
c++;
for (i = 0; i < delay; i++)
dummy++;
}
pthread_mutex_unlock(&mutex);
/************** end of critical section **************/
if (r_wait(NULL) == -1)
return 1;
...
*鎖互斥的初始化
.靜態(tài)初始化
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
.動態(tài)初始化
static pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
*使用形式
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
... /* 需要保護(hù)的操作 */
pthread_mutex_unlock(&lock);
*加鎖函數(shù)
//如果出錯則阻塞
pthread_mutex_lock(pthread_mutex_t *lock)
//如果出錯立即返回,errno=EBUSY
pthread_mutex_trylock(pthread_mutex_t *lock)
1.2 條件變量
如果沒有條件變量線程可能一直阻塞或輪訓(xùn)的方式來進(jìn)行相互的操作,也就是說線程根本就不知道是否可以進(jìn)行下面的操作而,只有進(jìn)行輪訓(xùn)去查詢看是否滿足條件。
條件變量和互斥鎖結(jié)合才不至于線程進(jìn)行忙等。
*相關(guān)函數(shù)1
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mpthr);
注意:
.在使用該函數(shù)前必須要用互斥鎖鎖住線程,然后再調(diào)用該函數(shù)。
.在沒有返回前該函數(shù)會自動釋放mpthr鎖。
.該函數(shù)會阻塞在條件變量cptr上,
.返回時,該函數(shù)又自動加上鎖。
.一般的使用結(jié)構(gòu)
pthread_mutex_lock(&l);
while (condition is true)
pthread_cond_wait(&cond, &l);
pthread_mutex_unlock(&l);
*相關(guān)函數(shù)2
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
in pthread_cond_signal(pthread_cond_t *cptr);
注意:
.第一個函數(shù)解除了所有阻塞在cond上的條件變量上的線程的阻塞。
.第二個函數(shù)解除了至少一個阻塞在cond指向的條件變量上的線程。
.調(diào)用這兩個函數(shù)線程可以沒有被加鎖,但如果沒有阻塞在條件變量的線程,那么它們不起任何作用。
.如果有多個線程阻塞在條件變量cond上,那么那個先運(yùn)行決定系統(tǒng)的調(diào)度。
實(shí)例:
小結(jié): 互斥鎖和條件變量一般用于同一進(jìn)程的不同線程間的同步, 用法比較簡單,應(yīng)用比較廣,比較重要的就是環(huán)形緩沖區(qū)模型,以及生產(chǎn)者消費(fèi)者模型。
2 posix信號燈
信號燈是一種提供不同進(jìn)程間或一個給定進(jìn)程的不同線程間同步的原語。其中有三種信號燈比較常用:
.posix有名信號燈
posix有名信號燈使用posix IPC名字,可用于多個進(jìn)程和線程間的同步。
.posix內(nèi)存信號燈
posix內(nèi)存信號燈存放在內(nèi)存區(qū)中,可用于多個線程間的同步;如果多個進(jìn)程間有共享內(nèi)存,而信號燈變量在共享
內(nèi)存區(qū)內(nèi),那么posix內(nèi)存信號燈也可以用于多個進(jìn)程間的同步。
.system v 信號燈:在內(nèi)核中維護(hù),可用于進(jìn)程或線程的同步。
*二值信號燈和計數(shù)信號燈
信號燈可以分為二值信號燈和計數(shù)信號燈。二值信號燈只有兩個值0或1,而計數(shù)信號燈的值可以大于1。
*對信號的操作
.創(chuàng)建信號一個信號燈。通常還需要調(diào)用者指定初始值,對于二值信號燈來說,它通常是1。對于計數(shù)信號燈要具體情況具體設(shè)定。
.等待一個信號燈(P操作)。該操作測試信號燈的值,如果其值小于或等于0,那就阻塞,一旦其值大于0就將它減1。而且該操作必須是一個原子操作。
.掛出一個信號燈(V操作)。該操作將信號燈的值加1,如果有一些進(jìn)程等待該信號燈的值變?yōu)榇笥?,其中一個進(jìn)程現(xiàn)在就可能被喚醒。該操作也必須是一個原子操作,所做的事情有:信號燈+1;解鎖;發(fā)信號。
*信號燈的使用模型
.互斥鎖模式
sem = 1;
sem_wait(&sem);
臨界區(qū)
sem_post(&sem);
.生產(chǎn)者,消費(fèi)者模型
生產(chǎn)者 消費(fèi)者
get = 0;
put = 1;
for ( ; ; ) { for ( ; ; ) {
sem_wait(&put); sem_wait(&get);
把數(shù)據(jù)放入緩沖區(qū)中 處理緩沖區(qū)中的數(shù)據(jù)
sem_post(&get); sem_post(&put);
} }
.注意 :
下面這種模型是錯誤的:
sem_t mysem;
sem_init(&mysem, 1, 0); //the 2nd arg of 1 : shared between processes
if (fork() == 0) { //child
...
sem_post(&mysem);
}
sem_wait(&mysem); //parent
想想為什么?
*信號燈和互斥鎖的差異:
.互斥鎖必須是由給他上鎖的線程解鎖,信號燈的掛出不必由執(zhí)行過它的等待操作的同一線程執(zhí)行。
.在使用互斥鎖時,如果線程發(fā)送了一個信號,但是沒有線程在等待這個信號的到來,那么這個信號就會丟失。但信號量將會記下這次的信號量。不會丟失。
.
*有名信號量和基于內(nèi)存的信號量的函數(shù)調(diào)用
有名信號燈 基于內(nèi)存的信號燈
sem_open() sem_init()
/ /
sem_wait()
sem_trywait()
sem_post()
sem_getvalue()
/ /
sem_close() sem_destory()
sem_unlink()
2.1 有名信號燈