linux多線程學習筆記四---線程同步之互斥鎖、讀寫鎖和條件變量

一,使用互斥鎖

1,初始化互斥量

pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//靜態初始化互斥量
int pthread_mutex_init(pthread_mutex_t*mutex,pthread_mutexattr_t*attr);//動態初始化互斥量
int pthread_mutex_destory(pthread_mutex_t*mutex);//撤銷互斥量

不能拷貝互斥量變量,但可以拷貝指向互斥量的指針,這樣就可以使多個函數或線程共享互斥量來實現同步。上面動態申請的互斥量需要動態的撤銷。

2,加鎖和解鎖互斥量

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t*mutex);
當調用pthread_mutex_lock加鎖互斥量時,如果此時互斥量已經被鎖住,則調用線程將被阻塞。而pthread_mutex_trylock函數當調用互斥量已經被鎖住時調用該函數將返回錯誤代碼EBUSY。使用和信號量一樣,先鎖住互斥量再處理共享數據,最後解鎖互斥量。

針對上信號量中的實例進行修改得

#include<pthread.h>
#include<stdio.h>
#include<semaphore.h>
#define NITERS 100000000
/*共享變量*/
unsigned int cnt = 0;
//sem_t mutex;
pthread_mutex_t mutex;
void *count(void *arg)
{
	int i;
	for(i=0;i<NITERS;i++)
	{
		//sem_wait(&mutex);
		pthread_mutex_lock(&mutex);
		cnt++;
		pthread_mutex_unlock(&mutex);
		//sem_post(&mutex);
	}
	return arg;
}
int main()
{
	pthread_t tid1,tid2;
	int status;
//	sem_init(&mutex,0,1);
	pthread_mutex_init(&mutex,NULL);
      
	pthread_mutex_destroy(&mutex);
	if(cnt!=(unsigned)NITERS*2)
		printf("Boom!,cnt=%d\n",cnt);
	else
		printf("Ok cnt=%d\n",cnt);
	return 0;
}

3,使用多個互斥量

使用多個互斥量可能造成死鎖問題。如下:

線程1                                                                                  線程2

pthread_mutex_lock(&mutex_a);                     pthread_mutex_lock(&mutex_b);

pthread_mutex_lock(&mutex_b);                     pthread_mutex_lock(&mutex_a);

當兩個線程都完成第一步時,都無法完成第二步,將造成死鎖。可以通過以下兩種方法來避免死鎖;

固定加鎖層次:所有需要同時加鎖互斥量A和互斥量B的代碼,必須先加鎖A再加鎖B。

試加鎖和回退:在鎖住第一個互斥量後,使用pthread_mutex_trylock來加鎖其他互斥量,如果失敗則將已加鎖的互斥量釋放,並重新加鎖。

二,使用讀寫鎖

通過讀寫鎖,可以對受保護的共享資源進行併發讀取和獨佔寫入。讀寫鎖是可以在讀取或寫入模式下鎖定的單一實體。要修改資源,線程必須首先獲取互斥寫鎖。必須釋放所有讀鎖之後,才允許使用互斥寫鎖。

1. 初始化和銷燬:

#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);

同互斥量一樣, 在釋放讀寫鎖佔用的內存之前, 需要先通過pthread_rwlock_destroy對讀寫鎖進行清理工作, 釋放由init分配的資源.

2.加鎖和解鎖

讀取讀寫鎖中的鎖 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
讀取非阻塞讀寫鎖中的鎖 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
寫入讀寫鎖中的鎖   int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
寫入非阻塞讀寫鎖中的鎖    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
解除鎖定讀寫鎖 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

三,條件變量

假如某個線程需要等待系統處於某種狀態下才能繼續執行,Linux爲了解決這種問題引入了條件變量這種線程同步對象,條件變量是用來通知共享數據狀態信息的,等待條件變量總是返回鎖住的互斥量,條件變量是與互斥量相關、也與互斥量保護的共享數據相關的信號機制。條件變量不提供互斥,需要一個互斥量來同步對共享數據的訪問,這就是爲什麼在等待條件變量時必須指定一個互斥量。

1)創建和銷燬條件變量

#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);     
2)等待條件變量

#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

兩個函數的差別在於前者指定一個超時時間,在該時間內阻塞調用線程,並等待條件變量,如果規定時間內條件還沒有發生,則函數返回。每個條件變量必須一個特定互斥量關聯,當線程等待條件變量時,他必須將相關互斥量鎖住。在阻塞線程之前,條件變量等待操作將解鎖互斥量,而在重新返回線程之前,會在次鎖住互斥量。

3)喚醒條件變量等待線程

#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal將會激活等待線程中的一個;pthread_cond_broadcast將會激活所有的線程。另外請注意這兩個函數也需要互斥量來保護



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章