互斥鎖pthread_mutex_t的使用

helloxchen發表於2010-11-12
互斥鎖pthread_mutex_t的使用
http://hi.baidu.com/cunlin/blog/item/1f9e3f384c6bbeffb211c79c.html

 有兩種方法建立互斥鎖,靜態方式和動態方式。POSIX定義了一個宏PTHREAD_MUTEX_INITIALIZER來靜態初始化互斥鎖,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads實現中,pthread_mutex_t是一個結構,而PTHREAD_MUTEX_INITIALIZER則是一個結構常量。

  動態方式是採用pthread_mutex_init()函式來初始化互斥鎖,API定義如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用於指定互斥鎖屬性(見下),如果為NULL則使用預設屬性。

  pthread_mutex_destroy ()用於登出一個互斥鎖,API定義如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 銷燬一個互斥鎖即意味著釋放它所佔用的資源,且要求鎖當前處於開放狀態。由於在Linux中,互斥鎖並不佔用任何資源,因此LinuxThreads中的 pthread_mutex_destroy()除了檢查鎖狀態以外(鎖定狀態則返回EBUSY)沒有其他動作。

  2. 互斥鎖屬性

  互斥鎖的屬性在建立鎖的時候指定,在LinuxThreads實現中僅有一個鎖型別屬性,不同的鎖型別在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。當前(glibc2.2.3,linuxthreads0.9)有四個值可供選擇:

  * PTHREAD_MUTEX_TIMED_NP,這是預設值,也就是普通鎖。當一個執行緒加鎖以後,其餘請求鎖的執行緒將形成一個等待佇列,並在解鎖後按優先順序獲得鎖。這種鎖策略保證了資源分配的公平性。

  * PTHREAD_MUTEX_RECURSIVE_NP,巢狀鎖,允許同一個執行緒對同一個鎖成功獲得多次,並透過多次unlock解鎖。如果是不同執行緒請求,則在加鎖執行緒解鎖時重新競爭。

  * PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個執行緒請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP型別動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。

  * PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖型別,僅等待解鎖後重新競爭。

  3. 鎖操作

  鎖操作主要包括加鎖pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個,不論哪種型別的鎖,都不可能被兩個不同的執行緒同時得到,而必須等待解鎖。對於普通鎖和適應鎖型別,解鎖者可以是同程式內任何執行緒;而檢錯鎖則必須由加鎖者解鎖才有效,否則返回EPERM;對於巢狀鎖,文件和實現要求必須由加鎖者解鎖,但實驗結果表明並沒有這種限制,這個不同目前還沒有得到解釋。在同一程式中的執行緒,如果加鎖後沒有解鎖,則任何其他執行緒都無法再獲得鎖。

  int pthread_mutex_lock(pthread_mutex_t *mutex)

  int pthread_mutex_unlock(pthread_mutex_t *mutex)

  int pthread_mutex_trylock(pthread_mutex_t *mutex)

  pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被佔據時返回EBUSY而不是掛起等待。

/*==============================================================================
bufsem.c
功能:主程式負責向佇列(就2個元素)寫入資料,建立的兩個執行緒負責讀取資料。
同步互斥:主程式和執行緒之間用訊號量sem_t進行同步,執行緒之間依靠互斥鎖進行互斥操作。
問題:程式和執行緒都使用了訊號量,可不可以免去互斥鎖?

注意:直接編譯gcc -o bufsem bufsem.c會出現如下錯誤

undefined reference to `sem_init'
undefined reference to `sem_post'
undefined reference to `sem_wait'

編譯選項需要加入一個多執行緒

gcc -pthread -o bufsem bufsem.c

*/==============================================================================

#include
#include
#include
#include
#include
#include

#define N 2 //佇列中元素的數量

struct _buf_
{
char *buf[N];
int front, rear; //有from和rear,顯然是個佇列
}BUF;

void * thread_function_r(void *arg); //執行緒函式
/*
訊號量的資料型別為結構sem_t,它本質上是一個長整型的數。函式sem_init()用來初始化一個訊號量。它的原型為:  
extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  
__sem為指向訊號量結構的一個指標;__pshared不為0時此訊號量在程式間共享,否則只能為當前程式的所有執行緒共享;__value給出了訊號量的初始值。  
*/
sem_t sem_r, sem_w; //semphore,訊號量,本程式用於保證程式和執行緒間的同步

pthread_mutex_t mutex; //互斥鎖,保證執行緒訪問的互斥,步驟一,定義資料型別

int main(int argc, char *argv[])
{

int res;
pthread_t a_thread_r, b_thread_r; //執行緒ID變數,與程式類似,程式為pid_t

for (res=0; resBUF.rear = BUF.front = 0;
/*訊號量用sem_init函式建立的,下面是它的說明:
  #include
int sem_init (sem_t *sem, int pshared, unsigned int value);
這個函式的作用是對由sem指定的訊號量進行初始化,設定好它的共享選項,並指定一個整數型別的初始值。pshared引數控制著訊號量的型別。如果 pshared的值是0,就表示它是當前里程的區域性訊號量;否則,其它程式就能夠共享這個訊號量。
*/
res = sem_init(&sem_r, 0, 0);
if ( res != 0)
{
perror(strerror(errno));
exit(EXIT_FAILURE);
}
res = sem_init(&sem_w, 0, 2);
if ( res != 0)
{
perror(strerror(errno));
exit(EXIT_FAILURE);
}

if ( pthread_mutex_init(&mutex, NULL) != 0 ) //互斥鎖,保證執行緒訪問的互斥,步驟二,初始化,預設屬性初始化互斥量——NULL引數的功能
{
perror(strerror(errno));
exit(EXIT_FAILURE);
}
/*
#include
  int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void*(*start_rtn)(void*),void *restrict arg);
  返回值:若成功則返回0,否則返回出錯編號
  返回成功時,由tidp指向的記憶體單元被設定為新建立執行緒的執行緒ID。attr引數用於制定各種不同的執行緒屬性。新建立的執行緒從start_rtn函式的地址開始執行,該函式只有一個無型別指標引數arg,如果需要向start_rtn函式傳遞的引數不止一個,那麼需要把這些引數放到一個結構中,然後把這個結構的地址作為arg的引數傳入。
*/
res = pthread_create(&a_thread_r, NULL, thread_function_r, NULL); //建立讀取執行緒一
if ( res != 0 )
{
perror(strerror(errno));
exit(EXIT_FAILURE);
}
res = pthread_create(&b_thread_r, NULL, thread_function_r, NULL); //建立讀取執行緒二
if ( res != 0 )
{
perror(strerror(errno));
exit(EXIT_FAILURE);
}

while(1) //【功能】主程式執行寫入操作
{
sem_wait(&sem_w); //函式sem_wait( sem_t *sem )被用來阻塞當前執行緒直到訊號量sem的值大於0,解除阻塞後將sem的值減一,表明公共資源經使用後減少。函式sem_trywait ( sem_t *sem )是函式sem_wait()的非阻塞版本,它直接將訊號量sem的值減一。函式sem_destroy(sem_t *sem)用來釋放訊號量sem。
fputs("Input any words, its words must less than 30: ", stdout);
fgets(BUF.buf[BUF.rear], 30, stdin);
if (strcmp("endn", BUF.buf[(BUF.front) % N]) == 0 ) break;
BUF.rear = (BUF.rear + 1) % N;
sem_post(&sem_r); //函式sem_post( sem_t *sem )用來增加訊號量的值。當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中的一個執行緒不再阻塞,選擇機制同樣是由執行緒的排程策略決定的。
usleep(1);
}

return 0;
}

void * thread_function_r(void *arg) //【功能】執行緒負責讀取
{
while(1)
{
sem_wait(&sem_r);
pthread_mutex_lock(&mutex); //獲取互斥量(互斥鎖),另外有pthread_mutex_trylock嘗試對互斥量加鎖,如果失敗返回EBUSY
fprintf(stdout, "Print:BUF.buf[%d] by %u: %s", BUF.front,pthread_self(), BUF.buf[BUF.front]);
BUF.front = (BUF.front + 1) % N;
pthread_mutex_unlock(&mutex); //釋放互斥鎖
sem_post(&sem_w);
}

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1041362/,如需轉載,請註明出處,否則將追究法律責任。

相關文章