【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(¬_full ,&mutex);
}
int data = 0;
data = rand()%100;
list_push(phead,data);
printf("producer : %d\n", data);
count++;
pthread_cond_signal(¬_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(¬_empty, &mutex);
}
int data = 0;
list_pop(phead, &data);
printf("consumer: %d \n", data);
count--;
pthread_cond_signal(¬_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(¬_empty);
pthread_cond_destroy(¬_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
【9】Caffe學習系列:執行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
執行緒池中使用條件變數和訊號量的效能比較
面試的時候經常被問到互斥量,條件變數和訊號量之間的問題。比如前幾天華為面試就被問到互斥量和訊號量的區別,說到互斥量也可以使用一個二值訊號量來實現,什麼情況是隻能使用互斥量而不能使用訊號量的。這個問題當時我只回答出一種情況,想了解詳情的可自行百度。如面試官所說,訊