1. 程式人生 > >【Linux】執行緒總結:執行緒同步 -互斥鎖,條件變數,訊號量實現多生產者多消費者模型

【Linux】執行緒總結:執行緒同步 -互斥鎖,條件變數,訊號量實現多生產者多消費者模型

學習環境 :  Centos6.5 Linux 核心 2.6

Linux執行緒部分總結分為兩部分:(1)執行緒的使用 ,(2)執行緒的同步與互斥。

第一部分執行緒的使用主要介紹,執行緒的概念,建立執行緒,執行緒退出,以及執行緒的終止與分離。【完成】 地址:【Linux】執行緒總結:初識、建立、等待、終止、分離

第二部分主要介紹在多執行緒環境下,使用同步與互斥保護共享資源,有互斥鎖,條件變數,訊號量,以及讀寫鎖。

第二部分開始 (第二部分,拖了快三個月,終於要出生了)

互斥量

描述

互斥變數用來保證同一時刻只有一個執行緒訪問需要保護的程式碼,用來保證臨界資源的資料一致性。本質上可理解為是一把鎖,在訪問共享資源之前申請鎖,只有你一個拿著鑰匙,別人要進入就必須等你出去後把鑰匙交給他們才可以。一旦使用完畢就要釋放鎖資源,給別人一個機會。 在使用的時候要注意,申請鎖資源與釋放鎖資源的順序,避免死鎖的產生。

介面


#include<pthread>
/* 動態初始化 */
int pthread_mutex_init(       pthread_mutex_t* mutex, 
                        const pthread_mutexattr_t* mutexattr);
/* 銷燬 */
int pthread_mutex_destroy( pthread_mutex_t* 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);

上述介面的第一個引數 mutex 都是定義的 pthread_mutex_t結構體指標,用來操作目標互斥鎖。

互斥鎖在使用之前必須初始化,pthread_mutex_init函式用於動態初始化互斥鎖,mutexattr 引數指定互斥鎖的屬性。還可以靜態分配,設定為常量 PTHREAD_MUTEX_INITALIZER。如果是動態初始化則在使用完成後需要呼叫pthread_mutex_destory

釋放資源。

pthread_mutex_lock函式以原子操作的方式給一個互斥鎖加鎖。如果目標互斥鎖已經被鎖上,該函式的其餘呼叫者將被阻塞,直到互斥鎖的佔有者將其解鎖。

pthread_mutex_trylock函式非阻塞式加鎖,如果已經被加鎖時,該函式立即返回錯誤碼 EBUSY。

pthread_mutex_unlock函式以原子操作的方式的給一個互斥鎖解鎖。如果此時有其他執行緒在等待這個互斥鎖,解鎖後將會有一個執行緒獲得該鎖。

具體使用方法可參照部落格:執行緒安全與執行緒不安全,有一個互斥量的簡單示例。

條件變數

互斥鎖用於同步執行緒對共享資料的訪問,而條件變數用於線上程之間同步共享資料的值,給多個執行緒提供了一個會合的場所。

條件變數本身是由互斥量保護的。執行緒在改變條件狀態之前必須先鎖住。

介面

#include <pthread.h>

/* 動態初始化 */
int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
/* 銷燬資源*/
int pthread_cond_destroy(pthread_cond_t* cond);
/* 等待cond 條件發生 */
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
/* 通知 cond 條件發生,只通知一個執行緒 */
int pthread_cond_signal(pthread_cond_t* cond);
/* 通知cond條件發生,通知所有程序 */
int pthread_cond_broadcast(pthread_cond_t* cond);

靜態初始化, 設定巨集 PTHREAD_COND_INITIALIZER

使用例項:基於互斥鎖和條件變數單緩衝區(一次只能有一個物件操作)多生產者多消費者模型

有一個連結串列,限制最多可插入10個結點,
一個互斥鎖保護對連結串列的操作,
兩個條件變數通知滿 10 以及 為空。

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

/* 靜態初始化 鎖 和 條件變數*/
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty  = PTHREAD_COND_INITIALIZER;

#define MAXSIZE  10
int count = 0;

typedef struct node
{
    int data;
    struct node* next;
} node_t, *pnode_t, **ppnode_t;


/* 初始化連結串列的頭結點 */
void list_init(ppnode_t pphead)
{
    assert(pphead);
    *pphead = (pnode_t)malloc(sizeof(node_t));
    (*pphead)->next = NULL;
}

/* 頭插 */
void list_push(pnode_t phead, int data)
{
    assert(phead);
    pnode_t temp = (pnode_t)malloc(sizeof(node_t));
    if(temp){
        temp->data = data;
        temp->next = phead->next;
        phead->next = temp;
    }
}

int list_empty(pnode_t phead)
{
    return phead->next == NULL ? 1 : 0;
}
/* 頭刪並通過引數返回被刪除的元素 */ 
void list_pop(pnode_t phead, int* data)
{
    assert(phead);
    if(!list_empty(phead))
    {
        pnode_t temp = phead->next;
        phead->next = temp->next;
        *data = temp->data;
        free(temp);
    }
}

void list_clear(pnode_t phead)
{
    while(!list_empty)
    {
        int data;
        list_pop(phead, &data);
    }
}

void list_destory(ppnode_t phead)
{
    assert(phead);
    list_clear(*phead);
    free(phead);
    phead = NULL;
}


void list_print(pnode_t phead)
{
    pnode_t start = phead->next;
    while(start)
    {
        printf("%d ", start->data);
        start = start->next;
    }
    printf("\n");
}

void* producer(void* arg)
{
    pnode_t phead = (pnode_t)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        while(count == MAXSIZE)
        {
            pthread_cond_wait(&not_full ,&mutex);
        }
        int data = 0;
        data =  rand()%100;
        list_push(phead,data);
        printf("producer : %d\n", data);
        count++;
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&mutex);
    }

}
void* consumer(void* arg)
{
    pnode_t phead = (pnode_t)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        while(count == 0)
        {
             pthread_cond_wait(&not_empty, &mutex);
        }

        int data = 0;
        list_pop(phead, &data);
        printf("consumer: %d \n", data);
        count--;
        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}


int main()
{
    pnode_t phead;
    list_init(&phead);

    //pthread_mutex_init(&mutex, NULL);
    //pthread_cond_init(&cond, NULL);
    //pthread_cond_init(&empty, NULL);
    //
    /* 建立生產者執行緒和消費者執行緒 */
    pthread_t id1, id2, id3, id4;
    pthread_create(&id1, NULL, producer, (void*)phead);
    pthread_create(&id2, NULL, producer, (void*)phead);
    pthread_create(&id3, NULL, consumer, (void*)phead);
    pthread_create(&id4, NULL, consumer, (void*)phead);

    /* 執行緒等待回收資源 */
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    pthread_join(id3, NULL);
    pthread_join(id4, NULL);

    /* 銷燬鎖和條件變數資源 */
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&not_empty);
    pthread_cond_destroy(&not_full);

    return 0;
}

這裡寫圖片描述

訊號量

介面:

#include <semaphore.h>

/*初始化訊號量, pshared 為0表示在同一程序的執行緒間同步,value 指定訊號量的初始值*/
int sem_init(sem_t* sem, int pshared, unsigned int value);
/* 釋放訊號量佔有的核心資源 */
int sem_destory (sem_t* sem);
/* 讓訊號量-1 相當於 P 操作 申請資源 */
int sem_wait    (sem_t* sem);
/* sem_wait 的非阻塞版本 */
int sem_trywait (sem_t* sem);
/* 讓訊號量+1 相當於 V 操作 釋放資源*/
int sem_post    (sem_t* sem);

使用例項:基於環形佇列的多生產者多消費者模型:

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


/* 環形佇列可分配資源數目 */
#define RESOURCE 6

int queue[RESOURCE];
int in = 0;
int out = 0;


/* 可生產數目訊號量 , 初始值為環形佇列大小*/
sem_t sem_p;
/* 可消費數目訊號線 , 初始值為 0  */
sem_t sem_c;

/* 生產者與生產者之間的鎖 */
pthread_mutex_t mutex_p = PTHREAD_MUTEX_INITIALIZER;
/* 消費者與消費者之間的鎖 */
pthread_mutex_t mutex_c = PTHREAD_MUTEX_INITIALIZER;

/* 生產者數目,消費者數目*/
#define PRODUCER 5
#define CONSUMER 5


void* producer(void* arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_p);
        sem_wait(&sem_p);
        printf("producer begin ...\n");
        queue[in] = rand()%100;
        int data = queue[in];
        in = (in+1) % RESOURCE;
        printf("producer %d  [done] \n", data);
        sem_post(&sem_c);
        pthread_mutex_unlock(&mutex_p);
    }
}
void* consumer(void* arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_c);
        sem_wait(&sem_c);
        printf("consumer begin ...\n");
        int data = queue[out];
        queue[out] = -1;
        out = (out+1) % RESOURCE;
        sleep(1);
        printf("consumer %d [done]\n", data);
        sem_post(&sem_p);
        pthread_mutex_unlock(&mutex_c);
    }
}

int main()
{

    sem_init(&sem_p, 0, RESOURCE);
    sem_init(&sem_c, 0, 0);
    pthread_mutex_init(&mutex_c, NULL);
    pthread_mutex_init(&mutex_p, NULL);
    pthread_t p[10];
    pthread_t c[10];

    /* 建立執行緒 */
    int i = 0;
    for(; i < PRODUCER; i++){
        pthread_create(&p[i], NULL, producer, NULL);
    }
    i = 0;
    for(; i < CONSUMER; i++){
        pthread_create(&c[i], NULL, consumer, NULL);
    }


    /* 回收執行緒  */
    i = 0;
    for(; i < PRODUCER ; i++){
        pthread_join(p[i], NULL);
    }
    i = 0;
    for(; i < CONSUMER ; i++){
        pthread_join(c[i], NULL);
    }

    sem_destroy(&sem_p);
    sem_destroy(&sem_c);
    pthread_mutex_destroy(&mutex_p);
    pthread_mutex_destroy(&mutex_c);
    return 0;

}

這裡寫圖片描述

擴充套件

還有讀寫鎖,用來處理讀者與寫者模型。讀者與寫者互斥與同步關係,讀者之間沒有關係,寫者之間互斥。

第二部分完

相關推薦

Linux執行程式設計---執行同步互斥條件變數訊號和讀寫

本篇博文轉自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。  下面是思維導

linux執行同步(通訊)的幾種方法——互斥條件變數訊號、讀寫

Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。  下面是思維導圖:  一、互斥鎖(mutex)    鎖機制是同一時刻只允許一個執行緒執行一個關鍵部分的程式碼。 1 . 初始化鎖 int pthread_mutex_init(p

Java併發程式設計之二十併發新特性—Lock條件變數(含程式碼)

簡單使用Lock鎖     Java 5中引入了新的鎖機制——java.util.concurrent.locks中的顯式的互斥鎖:Lock介面,它提供了比synchronized更加廣泛的鎖定操作。Lock介面有3個實現它的類:ReentrantLock、Reetrant

筆記程序間通訊——同步(互斥、讀寫條件變數訊號)以及Linux中的RCU

1.互斥鎖 多個執行緒的IPC,需要同步,同步有隱式的和顯示的: 比如unix提供的管道和FIFO,由核心負責同步,比如read發生在write之前,那麼read就會被核心阻塞,這中同步是由核心負責的,使用者不會感知。 但如果使用共享區作為生產者和消費者之間的IPC,那麼程

Linux執行總結執行同步 -互斥條件變數訊號實現生產者消費者模型

學習環境 :  Centos6.5 Linux 核心 2.6 Linux執行緒部分總結分為兩部分:(1)執行緒的使用 ,(2)執行緒的同步與互斥。 第一部分執行緒的使用主要介紹,執行緒的概念,建立執行緒,執行緒退出,以及執行緒的終止與分離。【完

Linux C 執行程式設計互斥條件變數

一、互斥鎖互斥量從本質上說就是一把鎖, 提供對共享資源的保護訪問。  1. 初始化:  在Linux下, 執行緒的互斥量資料型別是pthread_mutex_t. 在使用前, 要對它進行初始化:  對於靜態分配的互斥量, 可以把它設定為PTHREAD_MUTEX_INITIA

9Caffe學習系列執行caffe自帶的兩個簡單例子

為了程式的簡潔,在caffe中是不帶練習資料的,因此需要自己去下載。但在caffe根目錄下的data資料夾裡,作者已經為我們編寫好了下載資料的指令碼檔案,我們只需要聯網,執行這些指令碼檔案就行了。 注意:在caffe中執行所有程式,都必須在根目錄下進行,即/caffe,否則會出錯,因為指令碼檔案

19.執行同步訊號—>[單生產者/單消費者]單鏈表的插入和刪除

1.訊號量 1.訊號量本質 訊號量是鎖,是一種升級的mutex 訊號量在初始化時,可以指定共享資源的數量 2.相關函式 #include<semaphore.h> //標頭檔案 sem_t sem; //訊號量型別 int sem_destroy(se

Linux 常用命令總結(更新)

通過需求去總結一些命令是記憶的一個好方法,正在實踐中。 對檔案以及資料夾的操作命令: touch test.txt                                   //建立一個

Linux下面的執行,條件變數以及訊號的使用

一) 執行緒鎖1) 只能用於"鎖"住臨界程式碼區域2) 一個執行緒加的鎖必須由該執行緒解鎖.鎖幾乎是我們學習同步時最開始接觸到的一個策略,也是最簡單, 最直白的策略.二) 條件變數,與鎖不同, 條件變數用於等待某個條件被觸發1) 大體使用的偽碼:// 執行緒一程式碼pthread_mutex_lock

linux c 執行同步(通訊)的幾種方法--互斥條件變數訊號讀寫

轉載自:https://blog.csdn.net/vertor11/article/details/55657619Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。 下面是思維導圖: 一、互斥鎖(mutex)   鎖機制是同一時刻只允

Linux執行消費者生產者模型例項(互斥條件變數使用)

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

Linux執行程式設計互斥條件變數的簡單使用

Linux下的多執行緒遵循POSIX執行緒介面,稱為pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連結時需要使用庫libpthread.a。執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。執行緒

執行互斥條件變數例項理解

互斥鎖: 初始化程序鎖: int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 其中 mutex 為鎖號 attr為屬性 摧毀互

執行私有資料TSD——一鍵值技術執行同步中的互斥條件變數

一:執行緒私有資料: 執行緒是輕量級程序,程序在fork()之後,子程序不繼承父程序的鎖和警告,別的基本上都會繼承,而vfork()與fork()不同的地方在於vfork()之後的程序會共享父程序的地址空間,但是有了寫實複製(fork()之後的子程序也不會直接

轉載高併發解決常見併發同步案例分析

案例一:訂票系統案例,某航班只有一張機票,假定有1w個人開啟你的網站來訂票,問你如何解決併發問題(可擴充套件到任何高併發網站要考慮               的併發讀寫問題)    問題,1w個人來訪問,票沒出去前要保證大家都能看到有票,不可能一個人在看到票的時候別人就不能

程序間通訊機制(管道、訊號、共享記憶體/訊號/訊息佇列)、執行間通訊機制(互斥條件變數、posix匿名訊號

(1)系統中每個訊號量的資料結構(sem)struct sem {     int semval; /* 訊號量的當前值 */     unsigned short  semzcnt;  /* # waiting for zero */     unsigned short  semncnt;  /* # w

執行同步機制(互斥讀寫自旋條件變數屏障)

先知:      (1)執行緒是由程序建立而來,是cpu排程的最小單位。      (2)每個程序都有自己獨立的地址空間,而程序中的多個執行緒共用程序的資源,他們只有自己獨立的棧資源。 執行緒同步:      當多個控制執行緒共享相同的記憶體時,需要確保每個程序看到一致的

執行互斥條件變數實現生產者消費者-------迴圈任務佇列

互斥鎖與條件變數簡介 在多執行緒的環境中,全域性變數會被各執行緒共享,因此在操作全域性變數的時候需要採用鎖機制,在linux裡最常用的鎖就是互斥鎖,互斥鎖使用方法如下 <pre name="code" class="cpp">//執行緒A pthread_mut

執行池中使用條件變數訊號的效能比較

面試的時候經常被問到互斥量,條件變數和訊號量之間的問題。比如前幾天華為面試就被問到互斥量和訊號量的區別,說到互斥量也可以使用一個二值訊號量來實現,什麼情況是隻能使用互斥量而不能使用訊號量的。這個問題當時我只回答出一種情況,想了解詳情的可自行百度。如面試官所說,訊