1. 程式人生 > >pthread_mutex_t 和 pthread_cond_t 配合使用的簡要分析

pthread_mutex_t 和 pthread_cond_t 配合使用的簡要分析

開始 top 文章 cas main 釋放 void size condition

1.原理

假設有兩個線程同時訪問一個全局變量 n,這個全局變量的初始值等於0。

Int n = 0 ;

消費者線程 A 進入臨界區,訪問 n,A 必須等到 n 大於 0 才能接著往下執行,如果 n== 0,那麽 A 將一直等待。

還有一個生產者線程 B,B 進入臨界區,修改 n 的值,使得 n >0,當 n > 0 時,B 通知等待 n > 0 的消費者線程A。A 被 B 通知之後就可以接著往下執行了。

技術分享圖片


以上情況造成死鎖:

當 A 進入臨界區時,其他線程不能進入臨界區,意味著 B 沒有機會去修改 n, n 的值一直為 0,不滿足A 繼續執行的條件(n > 0),A 只能一直等待。

消費者進程拿到互斥鎖 --> 進入臨界區 --> 發現共享資源 n 不滿足繼續執行的條件(n> 0) --> 等待 n > 0

消費者進程占有互斥鎖 --> 生產者進程無法進入臨界區 --> 無法修改 n 的值 --> 生產者等待消費者釋放互斥鎖

解決死鎖的方案就是采用條件變量。

通常情況下,對共享資源(比如 n)保護要用到鎖操作,當一個進程進入臨界區時會拿到互斥鎖(lock 操作),然後其他進程拿不到互斥鎖,也就無法進入臨界區,因此當進程進入臨界區,發現共享資源不滿足繼續向下執行的條件(n > 0)時,就應該釋放鎖,讓其他進程修改共享資源,以滿足自己所需的執行條件。

消費者進入臨界區 --> 共享變量不滿足繼續向下執行的條件 --> 消費者等待在條件變量 --> 釋放互斥鎖 --> 生產者進入臨界區 --> 修改條件變量 --> 生產者通知消費者:現在有多的資源了,快來使用 --> 消費者再次拿互斥鎖 --> 消費資源 --> 釋放互斥鎖。如果有多個消費者進程等待在條件變量上,就可以形成等待隊列。

生產者和消費者模型中互斥鎖和條件變量的使用流程圖如下,其中藍色代表消費者的執行流,紅色是生產者的執行流。

技術分享圖片



2.使用方法

條件變量的使用主要有以下五個函數:

/* 初始化一個條件變量 */
int pthread_cond_init (pthread_cond_t* cond, pthread_condattr_t *cond_attr);

/* 銷毀一個條件變量 */
int pthread_cond_destroy(pthread_cond_t* cond);

/* 令一個消費者等待在條件變量上 */
int pthread_cond_wait(pthread_cond_t* cond);

/* 生產者通知等待在條件變量上的消費者 */
int pthread_cond_signal(pthread_cond_t* cond);

/* 生產者向消費者廣播消息 */
int pthread_cond_broadcast(pthread_cond_t* cond);


消費者等待條件的偽代碼:

pthread_mutex_lock(&mutex); // 拿到互斥鎖,進入臨界區
while( 條件為假)
pthread_cond_wait(cond, mutex); // 令進程等待在條件變量上
修改條件
pthread_mutex_unlock(&mutex); // 釋放互斥鎖





生產者通知消費者的偽代碼:

pthread_mutex_lock(&mutex); // 拿到互斥鎖,進入臨界區
設置條件為真
pthread_cond_signal(cond); // 通知等待在條件變量上的消費者
pthread_mutex_unlock(&mutex); // 釋放互斥鎖





以下是示例程序,演示了互斥鎖和條件變量配合使用方法,由於是在Linux下寫的程序,所以註釋全是英文的。

condition_test.c:

/***************************************************************
* Copyright (C) 2016 chengonghao
* All rights reserved.
*
* [email protected]
***************************************************************/
#include <unistd.h>
#include <pthread.h>

#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 1

pthread_mutex_t g_mutex ;
pthread_cond_t g_cond ;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT] ;
int share_variable = 0 ;// this is the share variable, shared by consumer and producer

void* consumer( void* arg )
{
int num = (int)arg ;
while ( 1 )
{
/******* critical section begin *******/
pthread_mutex_lock( &g_mutex ) ;

// if share_variable == 0, means consumer shell stop here
while ( share_variable == 0 )
{
printf( "consumer %d begin wait a condition...\n", num ) ;
// put a thread blocked ont a condition variable( here is g_cond),
// and unlock the mutex( here is g_mutex )
pthread_cond_wait( &g_cond, &g_mutex ) ;
}
// here means n != 0 and consumer can goes on
// consumer consumed shared variable, so the number of shared variable shell minus
printf( "consumer %d end wait a condition...\n", num ) ;
printf( "consumer %d begin consume product\n", num ) ;
-- share_variable ;

pthread_mutex_unlock( &g_mutex ) ;
/******** critial section end *********/
sleep( 1 ) ;
}

return NULL ;
}

void* producer( void* arg )
{
int num = (int)arg ;
while ( 1 )
{
/******* critical section begin *******/
pthread_mutex_lock( &g_mutex ) ;

// produce a shared variable
printf( "producer %d begin produce product...\n", num ) ;
++ share_variable ;
printf( "producer %d end produce product...\n", num ) ;
// unblock threads blocked on a condition variable( here is g_cond )
pthread_cond_signal( &g_cond ) ;
printf( "producer %d notified consumer by condition variable...\n", num ) ;
pthread_mutex_unlock( &g_mutex ) ;

/******** critial section end *********/
sleep( 5 ) ;
}

return 1 ;
}


int main( void )
{
// initiate mutex
pthread_mutex_init( &g_mutex, NULL ) ;
// initiate condition
pthread_cond_init( &g_cond, NULL ) ;

// initiate consumer threads
for ( int i = 0; i < CONSUMERS_COUNT; ++ i )
{
pthread_create( &g_thread[i], NULL, consumer, (void*)i ) ;
}
sleep( 1 ) ;
// initiate producer threads
for ( int i = 0; i < PRODUCERS_COUNT; ++ i )
{
pthread_create( &g_thread[i], NULL, producer, (void*)i ) ;
}
for ( int i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++ i )
{
pthread_join( g_thread[i], NULL ) ;
}

pthread_mutex_destroy( &g_mutex ) ;
pthread_cond_destroy( &g_cond ) ;
}





編譯程序:

cgh@ubuntu:~/condition_test$ gcc condition_test.c -o test –lpthread



運行程序:
技術分享圖片



1. 第一個框,消費者 1 和0 發現share_variable == 0,於是先後等待在條件變量上;

2. 第二個框,生產者 0 開始生產共享變量,即 ++ share_variable,然後通知等待在條件變量上的消費者;

3. 第三個框,消費者 1 被生產者喚醒,開始消費共享變量,即– share_variable;

4. 第四個框,生產者 0 繼續生產共享變量,++ share_variable,然後通知等待在條件變量上的消費者;

5. 第五個框,消費者 0 被喚醒,開始消費共享變量,即– share_variable;

以此類推,以上描述簡化了拿鎖和釋放鎖的過程,可以結合上面的流程圖來理解代碼。
---------------------
作者:chengonghao
來源:CSDN
原文:https://blog.csdn.net/chengonghao/article/details/51779279
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

pthread_mutex_t 和 pthread_cond_t 配合使用的簡要分析