【原創】《Linux高級程序設計》楊宗德著 - Linux多線程編程 - 線程同步機制


【原創】《Linux高級程序設計》楊宗德著 - Linux多線程編程 - 線程同步機制


互斥鎖基本原理

互斥以排他方式防止共享數據被併發修改。互斥鎖是一個二元變量,其狀態爲開鎖(允許0)和上鎖(禁止1),將某個共享資源與某個特定互斥鎖綁定後,對該共享資源的訪問如下操作:
(1)在訪問該資源前,首先申請該互斥鎖,如果該互斥處於開鎖狀態,則申請到該鎖對象,並立即佔有該鎖(使該鎖處於鎖定狀態),以防止其它線程訪問該資源;如果該互斥鎖處於鎖定狀態,默認阻塞等待;
(2)只有鎖定該互斥鎖的進程才能釋放該互斥鎖。其它線程的釋放操作無效。

互斥鎖基本操作函數 


1. 初始化互斥鎖

動態初始化


靜態初始化


2. 申請互斥鎖


3. 釋放互斥鎖


4. 銷燬互斥鎖

extern int pthread_mutex_destroy(pthtread_mutex_t *_mutex)

5. 互斥鎖應用實例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

void *thread_function(void *arg);

pthread_mutex_t work_mutex; 

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

int main(int argc,char *argv[]) 
{
    int res;
    pthread_t a_thread;
    void *thread_result;
    res = pthread_mutex_init(&work_mutex, NULL); //init mutex 
    if (res != 0) 
    {
        perror("Mutex initialization failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_create(&a_thread, NULL, thread_function, NULL);//create new thread
    if (res != 0) 
    {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    pthread_mutex_lock(&work_mutex);			//lock the mutex
    printf("Input some text. Enter 'end' to finish\n");
    while(!time_to_exit) 
    {
        fgets(work_area, WORK_SIZE, stdin);		//get a string from stdin
        pthread_mutex_unlock(&work_mutex);		//unlock the mutex
        while(1) 
       {
            pthread_mutex_lock(&work_mutex);	//lock the mutex
            if (work_area[0] != '\0') 
            {
                pthread_mutex_unlock(&work_mutex);	//unlock the mutex
                sleep(1);
            }
            else 
            {
                break;
            }
        }
    }
    pthread_mutex_unlock(&work_mutex);
    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) 
   {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    pthread_mutex_destroy(&work_mutex);
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) 
{
    sleep(1);
    pthread_mutex_lock(&work_mutex);
    while(strncmp("end", work_area, 3) != 0) 
    {
        printf("You input %d characters\n", strlen(work_area) -1);
        printf("the characters is %s",work_area);
        work_area[0] = '\0';
        pthread_mutex_unlock(&work_mutex);
        sleep(1);
        pthread_mutex_lock(&work_mutex);
        while (work_area[0] == '\0' ) 
        {
            pthread_mutex_unlock(&work_mutex);
            sleep(1);
            pthread_mutex_lock(&work_mutex);
        }
    }
    time_to_exit = 1;
    work_area[0] = '\0';
    pthread_mutex_unlock(&work_mutex);
    pthread_exit(0);
}
運行結果:

$ ./mutex_example 
Input some text. Enter 'end' to finish
hello mutex
You input 11 characters
the characters is hello mutex
this is a sample
You input 16 characters
the characters is this is a sample
end

Waiting for thread to finish...
Thread joined

條件變量通信機制

互斥鎖不能解決的問題 


如果只使用互斥鎖,可能導致do_something()永遠不會執行,這是程序員所不期望的,如下分析所示:
線程A搶佔到互斥鎖,執行操作,完成後i==4,j=6;然後釋放互斥鎖;
線程A和線程B都有可能搶佔到鎖,如果B搶佔到,條件不滿足,退出;如果線程A搶佔到,則執行操作,完成後i==5,j=5;然後釋放互斥鎖;
同理,線程A和線程B都有可能搶佔到鎖,如果B搶佔到,則條件滿足,do_something()得以執行,得到預期結果。但如果此時A沒有搶佔到,執行操作後i=6,j=4,此後i等於j的情況永遠不會發生。

條件變量解決的問題


條件變量基本操作 


include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int    pthread_cond_init(pthread_cond_t    *cond,    pthread_condattr_t
*cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int   pthread_cond_timedwait(pthread_cond_t   *cond,    pthread_mutex_t
*mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
說明
    條件變量是一種同步機制,允許線程掛起,直到共享數據上的某些條件得到滿足。條件變量上的基本操作有:觸發條件(當條件變爲 true 時);等待條件,掛起線程直到其他線程觸發條件。
    條件變量要和互斥量相聯結,以避免出現條件競爭--一個線程預備等待一個條件變量,當它在真正進入等待之前,另一個線程恰好觸發了該條件。
    pthread_cond_init 使用 cond_attr 指定的屬性初始化條件變量 cond,當 cond_attr 爲 NULL 時,使用缺省的屬性。LinuxThreads 實現條件變量不支持屬性,因此 cond_attr 參數實際被忽略。
    pthread_cond_t 類型的變量也可以用 PTHREAD_COND_INITIALIZER 常量進行靜態初始化。                                                                                          
    pthread_cond_signal 使在條件變量上等待的線程中的一個線程重新開始。如果沒有等待的線程,則什麼也不做。如果有多個線程在等待該條件,只有一個能重啓動,但不能指定哪一個。
    pthread_cond_broadcast 重啓動等待該條件變量的所有線程。如果沒有等待的線程,則什麼也不做。
    pthread_cond_wait 自動解鎖互斥量(如同執行了 pthread_unlock_mutex),並等待條件變量觸發。這時線程掛起,不佔用 CPU 時間,直到條件變量被觸發。在調用 pthread_cond_wait 之前,應用程序必須加鎖互斥量。pthread_cond_wait 函數返回前,自動重新對互斥量加鎖(如同執行了 pthread_lock_mutex)。
    互斥量的解鎖和在條件變量上掛起都是自動進行的。因此,在條件變量被觸發前,如果所有的線程都要對互斥量加鎖,這種機制可保證在線程加鎖互斥量和進入等待條件變量期間,條件變量不被觸發。
    pthread_cond_timedwait 和 pthread_cond_wait 一樣,自動解鎖互斥量及等待條件變量,但它還限定了等待時間。如果在 abstime 指定的時間內 cond 未觸發,互斥量 mutex 被重新加鎖,且 pthread_cond_timedwait 返回錯誤 ETIMEDOUT。abstime 參數指定一個絕對時間,時間原點與 time 和 gettimeofday 相同:abstime = 0 表示 1970 年 1 月 1 日 00:00:00 GMT。
    pthread_cond_destroy 銷燬一個條件變量,釋放它擁有的資源。進入 pthread_cond_destroy 之前,必須沒有在該條件變量上等待的線程。在 LinuxThreads 的實現中,條件變量不聯結資源,除檢查有沒有等待的線程外,pthread_cond_destroy 實際上什麼也不做。
取消                                                                                          
    pthread_cond_wait 和 pthread_cond_timedwait 是取消點。如果一個線程在這些函數上掛起時被取消,線程立即繼續執行,然後再次對 pthread_cond_wait 和 pthread_cond_timedwait 在 mutex 參數加鎖,最後執行取消。因此,當調用清除處理程序時,可確保,mutex 是加鎖的。
異步信號安全(Async-signal Safety)                                                                                          
    條件變量函數不是異步信號安全的,不應當在信號處理程序中進行調用。特別要注意,如果在信號處理程序中調用 pthread_cond_signal 或 pthread_cond_boardcast 函數,可能導致調用線程死鎖。

pthread_cond_signal 此函數被調用是隱含了釋放當前線程佔用的信號量的操作。

返回值
    在執行成功時,所有條件變量函數都返回 0,錯誤時返回非零的錯誤代碼。
錯誤代碼
    pthread_cond_init,   pthread_cond_signal,  pthread_cond_broadcast, 和 pthread_cond_wait 從不返回錯誤代碼。
    pthread_cond_timedwait 函數出錯時返回下列錯誤代碼:
        ETIMEDOUT   abstime 指定的時間超時時,條件變量未觸發
        EINTR       pthread_cond_timedwait 被觸發中斷
    pthread_cond_destroy 函數出錯時返回下列錯誤代碼:
        EBUSY       某些線程正在等待該條件變量

需要強調的是

條件變量不能單獨使用,必須配合互斥鎖一起實現對資源的互斥訪問。

條件變量應用實例

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pthread.h"

#define BUFFER_SIZE 2

/* Circular buffer of integers. */
struct prodcons 
{
	int buffer[BUFFER_SIZE];      /* the actual data */
	pthread_mutex_t lock;         /* mutex ensuring exclusive access to buffer */
	int readpos, writepos;        /* positions for reading and writing */
	pthread_cond_t notempty;      /* signaled when buffer is not empty */
	pthread_cond_t notfull;       /* signaled when buffer is not full */
};

/* Initialize a buffer */
void init(struct prodcons *prod)
{
	pthread_mutex_init(&prod->lock,NULL);
	pthread_cond_init(&prod->notempty,NULL);
	pthread_cond_init(&prod->notfull,NULL);
	prod->readpos = 0;
	prod->writepos = 0;
}
/* Store an integer in the buffer */
void put(struct prodcons * prod, int data)
{
	pthread_mutex_lock(&prod->lock);
	/* Wait until buffer is not full */
	while ((prod->writepos + 1) % BUFFER_SIZE == prod->readpos) 
	{
		printf("producer wait for not full\n");
		pthread_cond_wait(&prod->notfull, &prod->lock);
	}
	 /* Write the data and advance write pointer */
	prod->buffer[prod->writepos] = data;
	prod->writepos++;
	if (prod->writepos >= BUFFER_SIZE) 
		prod->writepos = 0;
	/*Signal that the buffer is now not empty */
	pthread_cond_signal(&prod->notempty);
	pthread_mutex_unlock(&prod->lock);
}
/* Read and remove an integer from the buffer */
int get(struct prodcons *prod)
{
	int data;
	pthread_mutex_lock(&prod->lock);
	/* Wait until buffer is not empty */
	while (prod->writepos == prod->readpos) 
	{
		printf("consumer wait for not empty\n");
		pthread_cond_wait(&prod->notempty, &prod->lock);
	}
	/* Read the data and advance read pointer */
	data = prod->buffer[prod->readpos];
	prod->readpos++;
	if (prod->readpos >= BUFFER_SIZE) 
		prod->readpos = 0;
	/* Signal that the buffer is now not full */
	pthread_cond_signal(&prod->notfull);
	pthread_mutex_unlock(&prod->lock);
	return data;
}

#define OVER (-1)
struct prodcons buffer;
/*--------------------------------------------------------*/
void * producer(void * data)
{
	int n;
	for (n = 0; n < 5; n++) 
	{
		printf("producer sleep 1 second......\n");
		sleep(1);
		printf("put the %d product\n", n);
		put(&buffer, n);
	}
	for(n=5; n<10; n++)
	{
		printf("producer sleep 3 second......\n");
		sleep(3);
		printf("put the %d product\n",n);
		put(&buffer,n);
	}
	put(&buffer, OVER);
	printf("producer stopped!\n");
	return NULL;
}
/*--------------------------------------------------------*/
void * consumer(void * data)
{
	int d=0;
	while (1) 
	{
		printf("consumer sleep 2 second......\n");
		sleep(2);
		d=get(&buffer);
		printf("get the %d product\n", d);
		//d = get(&buffer);
		if (d == OVER ) break;
	}
	printf("consumer stopped!\n");
	return NULL;
}
/*--------------------------------------------------------*/
int main(int argc,char *argv[])
{
	pthread_t th_a, th_b;
	void * retval;
	init(&buffer);
	pthread_create(&th_a, NULL, producer, 0);
	pthread_create(&th_b, NULL, consumer, 0);
	/* Wait until producer and consumer finish. */
	pthread_join(th_a, &retval);
	pthread_join(th_b, &retval);
	return 0;
}
運行結果:

$ ./pthread_cond_example 
consumer sleep 2 second......
producer sleep 1 second......
put the 0 product
producer sleep 1 second......
get the 0 product
consumer sleep 2 second......
put the 1 product
producer sleep 1 second......
put the 2 product
producer wait for not full
get the 1 product
consumer sleep 2 second......
producer sleep 1 second......
put the 3 product
producer wait for not full
get the 2 product
consumer sleep 2 second......
producer sleep 1 second......
put the 4 product
producer wait for not full
get the 3 product
consumer sleep 2 second......
producer sleep 3 second......
get the 4 product
consumer sleep 2 second......
put the 5 product
producer sleep 3 second......
get the 5 product
consumer sleep 2 second......
consumer wait for not empty
put the 6 product
producer sleep 3 second......
get the 6 product
consumer sleep 2 second......
consumer wait for not empty
put the 7 product
producer sleep 3 second......
get the 7 product
consumer sleep 2 second......
consumer wait for not empty
put the 8 product
producer sleep 3 second......
get the 8 product
consumer sleep 2 second......
consumer wait for not empty
put the 9 product
producer wait for not full
get the 9 product
consumer sleep 2 second......
producer stopped!
get the -1 product
consumer stopped!

讀寫鎖通信機制

概述
讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。互斥量要麼是鎖住狀態,要麼是不加鎖狀態,而且一次只有一個線程對其加鎖。讀寫鎖可以有三種狀態:讀模式下加鎖狀態,寫模式下加鎖狀態,不加鎖狀態。一次只有一個線程可以佔有寫模式的讀寫鎖,但是多個線程可用同時佔有讀模式的讀寫鎖。讀寫鎖也叫做共享-獨佔鎖,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的,當它以寫模式鎖住時,它是以獨佔模式鎖住的。

在對數據的讀寫應用中,更多的是讀操作,而寫操作較少,例如對數據庫數據的讀寫應用。爲了滿足當前能夠允許多個讀出,但只允許一個寫入的需求,線程提供了讀寫鎖來實現。其基本原則如下:

(1)如果有其它線程讀數據,則允許其它線程執行讀操作,但不允許寫操作;
(2)如果有其它線程寫數據,則其它線程的讀、寫操作均允許。
因此,其將該鎖分爲了讀鎖和寫鎖。
(1)如果某線程申請了讀鎖,其它線程可以再申請讀鎖,但不能申請寫鎖;
(2)如果某線程申請了寫鎖,則其它線程不能申請讀鎖,也不能申請寫鎖。
定義讀寫鎖對象的代碼如下:

pthread_rwlock_t rwlock;			//全局變量
讀寫鎖操作函數


讀寫鎖API
讀寫鎖的數據類型爲pthread_rwlock_t。如果這個類型的某個變量是靜態分配的,那麼可通過給它賦常值PTHREAD_RWLOCK_INITIALIZER來初始化它。
阻塞獲取讀寫鎖:

int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr); //獲取一個讀出鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr); //獲取一個寫入鎖
都返回:成功時爲0,出錯時爲正的Exxx值

非阻塞獲取讀寫鎖:

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr);
都返回:成功時爲0,出錯時爲正的Exxx值

解鎖:

int pthread_rwlock_unlock(pthread_rwlock_t *rwptr); //釋放一個寫入鎖或者讀出鎖
初始化和銷燬讀寫鎖:
int pthread_rwlock_init(pthread_rwlock_t *rwptr, const pthread_rwlockattr_t *attr)
int pthread_rwlock_destroy(pthread_rwlock_t *rwptr);
都返回:成功時爲0,出錯時爲正的Exxx值
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
都返回:成功時爲0,出錯時爲正的Exxx值
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *valptr);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int valptr);
都返回:成功時爲0,出錯時爲正的Exxx值

讀寫鎖應用實例

#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <bits/pthreadtypes.h>

static pthread_rwlock_t rwlock;

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit;

void *thread_function_read_o(void *arg);
void *thread_function_read_t(void *arg);
void *thread_function_write_o(void *arg);
void *thread_function_write_t(void *arg);
	

int main(int argc,char *argv[]) 
{
	int res;
	pthread_t a_thread,b_thread,c_thread,d_thread;
	void *thread_result;

	res=pthread_rwlock_init(&rwlock,NULL);
	if (res != 0) 
	{
		perror("rwlock initialization failed");
		exit(EXIT_FAILURE);
	}
	res = pthread_create(&a_thread, NULL, thread_function_read_o, NULL);//create new thread
	if (res != 0) 
	{
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}

 	res = pthread_create(&b_thread, NULL, thread_function_read_t, NULL);//create new thread
	if (res != 0) 
	{
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}
	res = pthread_create(&c_thread, NULL, thread_function_write_o, NULL);//create new thread
	if (res != 0)
	{
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}
	res = pthread_create(&d_thread, NULL, thread_function_write_t, NULL);//create new thread
	if (res != 0)
	{
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}
	
 	res = pthread_join(a_thread, &thread_result);			
 	if (res != 0) 
 	{
		 perror("Thread join failed");
		 exit(EXIT_FAILURE);
 	}
 	res = pthread_join(b_thread, &thread_result);			
	if (res != 0) 
 	{
		 perror("Thread join failed");
		 exit(EXIT_FAILURE);
 	}
	res = pthread_join(c_thread, &thread_result);			
	if (res != 0) 
	{
		perror("Thread join failed");
		exit(EXIT_FAILURE);
	}
	res = pthread_join(d_thread, &thread_result);			
	if (res != 0) 
	{
	   perror("Thread join failed");
	   exit(EXIT_FAILURE);
	}
   
	pthread_rwlock_destroy(&rwlock);				
	exit(EXIT_SUCCESS);
}

void *thread_function_read_o(void *arg)
{
	printf("thread read one try to get lock\n");	
	
	pthread_rwlock_rdlock(&rwlock);
	while(strncmp("end", work_area, 3) != 0) 
	{
		printf("this is thread read one.");
		printf("the characters is %s",work_area);	
		pthread_rwlock_unlock(&rwlock);			
		sleep(2);
		pthread_rwlock_rdlock(&rwlock);			
		while (work_area[0] == '\0' ) 		 
		{
			pthread_rwlock_unlock(&rwlock);	
			sleep(2);
			pthread_rwlock_rdlock(&rwlock);
		}
	}	
	pthread_rwlock_unlock(&rwlock);	
	time_to_exit=1;
	pthread_exit(0);
}

 void *thread_function_read_t(void *arg)
{
	printf("thread read one try to get lock\n");
	pthread_rwlock_rdlock(&rwlock);
	while(strncmp("end", work_area, 3) != 0) 
	{
		printf("this is thread read two.");
		printf("the characters is %s",work_area);	
		pthread_rwlock_unlock(&rwlock);			
		sleep(5);
		pthread_rwlock_rdlock(&rwlock);			
		while (work_area[0] == '\0' ) 		 
		{				
			pthread_rwlock_unlock(&rwlock);	
			sleep(5);
			pthread_rwlock_rdlock(&rwlock);	
		}
	}
	pthread_rwlock_unlock(&rwlock);	
	time_to_exit=1;
	pthread_exit(0);
}

void *thread_function_write_o(void *arg)
{
	printf("this is write thread one try to get lock\n");
	while(!time_to_exit) 
	{
		pthread_rwlock_wrlock(&rwlock);
		printf("this is write thread one.\nInput some text. Enter 'end' to finish\n");
		fgets(work_area, WORK_SIZE, stdin);
		pthread_rwlock_unlock(&rwlock);
		sleep(15);
	}
	pthread_rwlock_unlock(&rwlock);
	pthread_exit(0);
}

void *thread_function_write_t(void *arg)
{
	sleep(10);
	while(!time_to_exit)
	{
		pthread_rwlock_wrlock(&rwlock);
		printf("this is write thread two.\nInput some text. Enter 'end' to finish\n"); 
		fgets(work_area, WORK_SIZE, stdin);
		pthread_rwlock_unlock(&rwlock);
		sleep(20);
	}
	pthread_rwlock_unlock(&rwlock);
	pthread_exit(0);
}
運行結果:

$ ./pthread_rwlock_example 
this is write thread one try to get lock
thread read one try to get lock
this is write thread one.
Input some text. Enter 'end' to finish
thread read one try to get lock
wr one test
this is thread read two.the characters is wr one test
this is thread read one.the characters is wr one test
this is thread read one.the characters is wr one test
this is thread read one.the characters is wr one test
this is thread read two.the characters is wr one test
this is write thread two.
Input some text. Enter 'end' to finish
wr two test
this is thread read one.the characters is wr two test
this is thread read two.the characters is wr two test
this is thread read one.the characters is wr two test
this is write thread one.
Input some text. Enter 'end' to finish
end

原文鏈接:

http://blog.csdn.net/geng823/article/details/41344735

發佈了116 篇原創文章 · 獲贊 16 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章