linux多執行緒-----同步機制(互斥量、讀寫鎖、條件變數)

readyao發表於2016-03-23


linux多執行緒同步機制主要有:互斥量,讀寫鎖,條件變數。

互斥量:

互斥量用pthread_mutex_t資料型別表示,在使用互斥變數以前,必須對它進行初始化,可以把它設定為PTHREAD_MUTEX_INITIALIZER(只是針對靜態分配的互斥量),pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
也可以通過函式int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);進行初始化。

pthread_mutex_destroy destroys a mutex object, freeing the resources it might hold. The mutex must be unlocked on entrance.  
In  the  LinuxThreads implementation, no resources are associated with mutex objects, thus pthread_mutex_destroy actually does 
nothing except checking that the mutex is unlocked.
man手冊關於pthread_mutex_destroy的介紹就是說在linux下執行緒的實現沒有關於互斥量物件的資源,所以pthread_mutex_destroy僅僅是確認一下互斥量mutex是處於解鎖的狀態。


int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);


1.pthread_mutex_lock函式對互斥量mutex進行加鎖,如果互斥量已經上鎖,呼叫執行緒將一直阻塞直到互斥量被解鎖。
2.pthread_mutex_unlock函式是對互斥量mutex進行解鎖。
3.pthread_mutex_trylock函式嘗試對互斥量mutex進行加鎖,如果互斥量mutex處於未被鎖住的狀態,則將鎖住該互斥量並返回0。如果互斥量mutex處於鎖住的狀態,則不能鎖住該互斥量並返回EBUSY。

例子1:對互斥量加上引用計數機制,自己構造結構體。當最後一個引用被釋放的時候,物件所佔用空間也被釋放。如下所示:

/*************************************************************************
	> File Name: mutex_11_5.c
	> Author: 
	> Mail: 
	> Created Time: 2016年03月23日 星期三 20時41分26秒
 ************************************************************************/

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

struct foo{
    int f_count;
    pthread_mutex_t f_lock;

};

//建立並初始化一個帶有引用計數的互斥量
struct foo * foo_alloc(void)
{
    struct foo * fp;
    
    if((fp = malloc(sizeof(struct foo))) != NULL){
        fp->f_count = 1;
        if(pthread_mutex_init(&fp->f_lock, NULL) != 0){
            free(fp);
            return NULL;
        }
    }

    return fp;
}

//對互斥量增加一個引用計數
void foo_hold(struct foo* fp)
{
    pthread_mutex_lock(&fp->f_lock);
    fp->f_count++;
    pthread_mutex_unlock(&fp->f_lock);
}


//對互斥量減少一個引用計數
void foo_rele(struct foo* fp)
{
    pthread_mutex_lock(&fp->f_lock);

    if(--fp->f_count == 0){//最後一個引用了
        printf("last the reference\n");
        pthread_mutex_unlock(&fp->f_lock);
        pthread_mutex_destroy(&fp->f_lock);
        free(fp);
    }
    else{
        pthread_mutex_unlock(&fp->f_lock);
    }
}


int main()
{
    struct foo * fp;

    if((fp = foo_alloc()) == NULL){
        printf("foo_alloc error\n");
        return -1;
    }

    printf("After foo_alloc, the fp->f_count = %d\n", fp->f_count);

    foo_hold(fp);
    printf("After foo_hold, the fp->f_count = %d\n", fp->f_count);

    foo_hold(fp);
    printf("After foo_hold, the fp->f_count = %d\n", fp->f_count);

    foo_rele(fp);
    printf("After foo_rele, the fp->f_count = %d\n", fp->f_count);

    foo_rele(fp);
    printf("After foo_rele, the fp->f_count = %d\n", fp->f_count);

    foo_rele(fp);
    printf("After foo_rele, the fp->f_count = %d\n", fp->f_count);

    return 0;
}
輸出:

After foo_alloc, the fp->f_count = 1
After foo_hold, the fp->f_count = 2
After foo_hold, the fp->f_count = 3
After foo_rele, the fp->f_count = 2
After foo_rele, the fp->f_count = 1
last the reference
After foo_rele, the fp->f_count = 0

讀寫鎖(也叫做共享-獨佔鎖):

讀寫鎖和互斥鎖類似,但是讀寫鎖允許更高的併發性。
互斥鎖只有兩種狀態:加鎖狀態、不加鎖狀態;
讀寫鎖有三種狀態:讀模式下加鎖狀態,寫模式下加鎖狀態,不加鎖狀態;
一次只能有一個執行緒佔有寫模式下的讀寫鎖,但是可以允許多個執行緒佔用讀模式下的讀寫鎖。

讀寫鎖非常適用於對資料結構讀的次數遠大於寫的情況。

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//對讀寫鎖進行初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);//釋放為讀寫鎖分配的資源

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//在讀模式下獲得讀寫鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//在寫模式下獲得讀寫鎖

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//嘗試在讀模式下獲得讀寫鎖
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//嘗試在寫模式下獲得讀寫鎖

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//對讀寫鎖進行解鎖

條件變數:

條件變數是執行緒可以使用的另一種同步機制。條件變數與互斥量一起使用的時候,允許執行緒以無競爭的方式等待特定的條件發生。
條件本身是由互斥量保護的。執行緒在改變條件變數狀態前必須先鎖住互斥量。

條件變數在使用前必須初始化,一種是靜態初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
另一種是動態分配的條件變數,則用pthread_cond_init函式進行初始化。
在釋放底層的記憶體空間之前,可以使用pthread_cond_destroy對條件變數進行去初始化。


pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_destroy(pthread_cond_t *cond);

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

執行緒呼叫pthread_cond_wait這個函式之後,核心會做下面這些事:
1,拿到鎖的執行緒,把鎖暫時釋放;
2,執行緒休眠,進行等待;
3,執行緒等待通知,要醒來。(重新獲取鎖)
pthread_cond_timedwait函式功能和pthread_cond_wait類似,只是多了一個等待時間abstime的限制。

條件變數的使用參考部落格:Linux多執行緒消費者和生產者模型例項(互斥鎖和條件變數使用)


相關文章