二、(LINUX 執行緒同步) 互斥量、條件變數以及生產者消費者問題
原創轉載請註明出處:
接上一篇:
一、(LINUX 執行緒同步) 引入 http://blog.itpub.net/7728585/viewspace-2137980/
線上程同步中我們經常會使用到mutex互斥量,其作用用於保護一塊臨界區,避免多執行緒併發操作對這片臨界區帶來的資料混亂,
POSIX的互斥量是一種建議鎖,因為如果不使用互斥量也可以訪問共享資料,但是可能是不安全的。
其原語包含:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
靜態初始一個互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
銷燬一個互斥量
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
初始化一個互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果不能獲得鎖則堵塞等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果獲得不了鎖則放棄
int pthread_mutex_unlock(pthread_mutex_t *mutex);
互斥量解鎖操作,一般用於在臨界區資料操作完成後解鎖
而條件變數cond則代表當某個條件不滿足的情況下,本執行緒應該放棄鎖,並且將本執行緒堵塞。典型的
生產者消費者問題,如果生產者還沒來得及生產東西,消費者則不應該進行消費操作,應該放棄鎖,將
自己堵塞,直到條件滿足被生產者喚醒。原語包含:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
靜態初始一個條件變數
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *cattr);
初始化一個條件變數
int pthread_cond_destroy(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_signal(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上某個執行緒,一般用於生產者喚醒消費者
int pthread_cond_broadcast(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上全部執行緒,一般用於生產者喚醒消費者
下面一張自己畫的圖希望對大家有所幫助。
最後是我寫的一個,用一個連結串列棧結構,來模擬一個生產者生產資料,N個消費者併發進行消費的
一個生產者消費者程式。生產者一次生產一批資料,消費者一次只消費一個資料。
當生產者一次生產10個資料的時候,輸出是這樣的。
p:thread:140034536785664 prod 1 data is:data A:0 data B:0
p:thread:140034536785664 prod 2 data is:data A:1 data B:1
p:thread:140034536785664 prod 3 data is:data A:2 data B:2
p:thread:140034536785664 prod 4 data is:data A:3 data B:3
p:thread:140034536785664 prod 5 data is:data A:4 data B:4
p:thread:140034536785664 prod 6 data is:data A:5 data B:5
p:thread:140034536785664 prod 7 data is:data A:6 data B:6
p:thread:140034536785664 prod 8 data is:data A:7 data B:7
p:thread:140034536785664 prod 9 data is:data A:8 data B:8
p:thread:140034536785664 prod 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 9 data is:data A:8 data B:8
c:thread:140034520000256 cost 8 data is:data A:7 data B:7
c:thread:140034520000256 cost 7 data is:data A:6 data B:6
c:thread:140034520000256 cost 6 data is:data A:5 data B:5
c:thread:140034520000256 cost 5 data is:data A:4 data B:4
c:thread:140034520000256 cost 4 data is:data A:3 data B:3
c:thread:140034520000256 cost 3 data is:data A:2 data B:2
c:thread:140034520000256 cost 2 data is:data A:1 data B:1
c:thread:140034520000256 cost 1 data is:data A:0 data B:0
也可以看到他確實是一個後進先出的棧模型
這個時候並沒有觀察到多個執行緒競爭的問題,如果我將生產者定義為一次生產10000個資料,就可以
看到多個執行緒交替使用生產者資料的情況。
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958 c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
我們可以看到不同的執行緒ID的執行緒在競爭這把鎖
下面是程式碼,臨界區應該儘量選擇小,我這裡臨界區選擇較大,比如連結串列的建立
c:thread:140270692525824 cost 9962 data is:data A:9961 data B:9961
c:thread:140270692525824 cost 9961 data is:data A:9960 data B:9960
c:thread:140270692525824 cost 9960 data is:data A:9959 data B:9959
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958
c:thread:140270541522688 cost 9958 data is:data A:9957 data B:9957
c:thread:140270541522688 cost 9957 data is:data A:9956 data B:9956
c:thread:140270541522688 cost 9956 data is:data A:9955 data B:9955
c:thread:140270541522688 cost 9955 data is:data A:9954 data B:9954
c:thread:140270541522688 cost 9954 data is:data A:9953 data B:9953
c:thread:140270541522688 cost 9953 data is:data A:9952 data B:9952
c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
其實不需要放到臨界區中,還比如記憶體的delete也不需要放到臨界區,處於測試
目的已經達到,就不在糾結這個問題了:
主檔案:
作者微信:
接上一篇:
一、(LINUX 執行緒同步) 引入 http://blog.itpub.net/7728585/viewspace-2137980/
線上程同步中我們經常會使用到mutex互斥量,其作用用於保護一塊臨界區,避免多執行緒併發操作對這片臨界區帶來的資料混亂,
POSIX的互斥量是一種建議鎖,因為如果不使用互斥量也可以訪問共享資料,但是可能是不安全的。
其原語包含:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
靜態初始一個互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
銷燬一個互斥量
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
初始化一個互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果不能獲得鎖則堵塞等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果獲得不了鎖則放棄
int pthread_mutex_unlock(pthread_mutex_t *mutex);
互斥量解鎖操作,一般用於在臨界區資料操作完成後解鎖
而條件變數cond則代表當某個條件不滿足的情況下,本執行緒應該放棄鎖,並且將本執行緒堵塞。典型的
生產者消費者問題,如果生產者還沒來得及生產東西,消費者則不應該進行消費操作,應該放棄鎖,將
自己堵塞,直到條件滿足被生產者喚醒。原語包含:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
靜態初始一個條件變數
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *cattr);
初始化一個條件變數
int pthread_cond_destroy(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_signal(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上某個執行緒,一般用於生產者喚醒消費者
int pthread_cond_broadcast(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上全部執行緒,一般用於生產者喚醒消費者
下面一張自己畫的圖希望對大家有所幫助。
最後是我寫的一個,用一個連結串列棧結構,來模擬一個生產者生產資料,N個消費者併發進行消費的
一個生產者消費者程式。生產者一次生產一批資料,消費者一次只消費一個資料。
當生產者一次生產10個資料的時候,輸出是這樣的。
p:thread:140034536785664 prod 1 data is:data A:0 data B:0
p:thread:140034536785664 prod 2 data is:data A:1 data B:1
p:thread:140034536785664 prod 3 data is:data A:2 data B:2
p:thread:140034536785664 prod 4 data is:data A:3 data B:3
p:thread:140034536785664 prod 5 data is:data A:4 data B:4
p:thread:140034536785664 prod 6 data is:data A:5 data B:5
p:thread:140034536785664 prod 7 data is:data A:6 data B:6
p:thread:140034536785664 prod 8 data is:data A:7 data B:7
p:thread:140034536785664 prod 9 data is:data A:8 data B:8
p:thread:140034536785664 prod 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 9 data is:data A:8 data B:8
c:thread:140034520000256 cost 8 data is:data A:7 data B:7
c:thread:140034520000256 cost 7 data is:data A:6 data B:6
c:thread:140034520000256 cost 6 data is:data A:5 data B:5
c:thread:140034520000256 cost 5 data is:data A:4 data B:4
c:thread:140034520000256 cost 4 data is:data A:3 data B:3
c:thread:140034520000256 cost 3 data is:data A:2 data B:2
c:thread:140034520000256 cost 2 data is:data A:1 data B:1
c:thread:140034520000256 cost 1 data is:data A:0 data B:0
也可以看到他確實是一個後進先出的棧模型
這個時候並沒有觀察到多個執行緒競爭的問題,如果我將生產者定義為一次生產10000個資料,就可以
看到多個執行緒交替使用生產者資料的情況。
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958 c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
我們可以看到不同的執行緒ID的執行緒在競爭這把鎖
下面是程式碼,臨界區應該儘量選擇小,我這裡臨界區選擇較大,比如連結串列的建立
c:thread:140270692525824 cost 9962 data is:data A:9961 data B:9961
c:thread:140270692525824 cost 9961 data is:data A:9960 data B:9960
c:thread:140270692525824 cost 9960 data is:data A:9959 data B:9959
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958
c:thread:140270541522688 cost 9958 data is:data A:9957 data B:9957
c:thread:140270541522688 cost 9957 data is:data A:9956 data B:9956
c:thread:140270541522688 cost 9956 data is:data A:9955 data B:9955
c:thread:140270541522688 cost 9955 data is:data A:9954 data B:9954
c:thread:140270541522688 cost 9954 data is:data A:9953 data B:9953
c:thread:140270541522688 cost 9953 data is:data A:9952 data B:9952
c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
其實不需要放到臨界區中,還比如記憶體的delete也不需要放到臨界區,處於測試
目的已經達到,就不在糾結這個問題了:
點選(此處)摺疊或開啟
-
標頭檔案:
-
/*************************************************************************
-
> File Name: chain.h
-
> Author: gaopeng QQ:22389860 all right reserved
-
> Mail: gaopp_200217@163.com
-
> Created Time: Sun 04 Jun 2017 06:27:38 AM CST
-
************************************************************************/
-
-
#include<iostream>
-
#include<stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
-
#define MAX_TID 10
-
using namespace std;
-
-
-
class t1data
-
{
-
private:
-
int a;
-
int b;
-
public:
-
t1data(){}
-
t1data(int i)
-
{
-
this->a = i;
-
this->b = i;
-
}
-
virtual void prin(void)
-
{
-
cout<<"data A:"<<a<<" data B:"<<b;
-
}
-
-
};
-
-
-
typedef struct queue_s
-
{
-
t1data data;
-
queue_s *next,*priv;
-
queue_s(int i)
-
{
-
data = t1data(i);
-
next = NULL;
-
priv = NULL;
-
}
-
} QUE_S,*QUE_S_P ;
-
-
typedef struct queue_h
-
{
-
QUE_S_P head_p;
-
QUE_S_P last_p;
-
unsigned int len;
-
pthread_mutex_t pmut;
-
pthread_cond_t pcon;
-
queue_h()
-
{
-
head_p=NULL;
-
last_p=NULL;
-
len = 0;
-
}
-
} QUE_H;
-
-
int debug_return(const int ret)
-
{
-
if(ret != 0)
-
{
-
strerror(ret);
-
exit(-1);
-
}
-
return 0;
- }
點選(此處)摺疊或開啟
-
/*************************************************************************
-
> File Name: main.cpp
-
> Author: gaopeng QQ:22389860 all right reserved
-
> Mail: gaopp_200217@163.com
-
> Created Time: Sun 04 Jun 2017 07:18:28 AM CST
-
************************************************************************/
-
-
#include<iostream>
-
#include<stdio.h>
-
#include <pthread.h>
-
#include<string.h>
-
#include"chain.h"
-
-
#define MAXDATA 10000
-
//雙向連結串列棧結構模型
-
-
using namespace std;
-
-
static int COUNT = 0;
-
-
-
void* pro(void* arg)
-
{
-
-
QUE_H* my_head;
-
my_head = (QUE_H*)arg;
-
QUE_S_P sp =NULL;
-
int ret = 0;
-
-
while(1)
-
{
-
ret = pthread_mutex_lock(&my_head->pmut);
-
debug_return(ret);
-
-
if(my_head->head_p != NULL)//如果元素沒有消費完放棄MUTEX,進入下次迴圈
-
{
-
//cout<<"pro head != NULL unlock\n";
-
ret = pthread_mutex_unlock(&my_head->pmut);
-
debug_return(ret);
-
continue;
-
}
-
//如果消費完進行生產10000個元素
-
for(;COUNT<MAXDATA;COUNT++)//生產10000個元素
-
{
-
if(my_head->head_p == NULL)
-
{
-
int tm = 0;
-
QUE_S_P sp = new QUE_S(COUNT);
-
my_head->head_p = sp;
-
my_head->last_p = sp;
-
cout<<"p:thread:"<<pthread_self()<<" prod "<<++my_head->len<<" data is:";
-
(my_head->last_p->data).prin();
-
cout<<"\n";
-
sp = NULL;
-
}
-
else
-
{
-
QUE_S_P sp = new QUE_S(COUNT);
-
my_head->last_p->next = sp;
-
sp->priv = my_head->last_p;
-
my_head->last_p = sp;
-
cout<<"p:thread:"<<pthread_self()<<" prod "<<++my_head->len<<" data is:";
-
my_head->last_p->data.prin();
-
cout<<"\n";
-
sp = NULL;
-
}
-
}
-
//cout<<"pro unlock:\n";
-
ret = pthread_mutex_unlock(&my_head->pmut);
-
debug_return(ret);
-
//cout<<"pro signal:\n";
-
ret = pthread_cond_signal(&my_head->pcon);
-
debug_return(ret);
-
-
}
-
-
}
-
-
void* cus(void* arg)
-
{
-
int ret = 0;
-
QUE_H* my_head;
-
my_head = (QUE_H*)arg;
-
QUE_S_P tmp=NULL;
-
-
while(1)//消費方式為一個消費執行緒只消費一個元素,來模擬多消費執行緒競爭
-
{
-
ret = pthread_mutex_lock(&my_head->pmut);
-
debug_return(ret);
-
while(my_head->head_p == NULL) //如果已經消費完放棄鎖,等到條件及有元素供消費
-
{
-
//cout<<"cus cond wait\n";
-
ret = pthread_cond_wait(&my_head->pcon,&my_head->pmut);
-
debug_return(ret);
-
}
-
-
if(my_head->len == 1)//消費如果只剩下一個元素處理
-
{
-
cout<<"c:thread:"<<pthread_self()<<" cost "<<my_head->len--<<" data is:";
-
my_head->last_p->data.prin();
-
delete my_head->last_p;
-
my_head->head_p = NULL;
-
my_head->last_p = NULL;
-
COUNT--;
-
}
-
else//否則處理如下
-
{
-
cout<<"c:thread:"<<pthread_self()<<" cost "<<my_head->len--<<" data is:";
-
my_head->last_p->data.prin();
-
cout<<"\n";
-
tmp = my_head->last_p->priv;
-
delete my_head->last_p;
-
my_head->last_p= tmp;
-
tmp=NULL;
-
COUNT--;
-
}
-
-
ret = pthread_mutex_unlock(&my_head->pmut);
-
debug_return(ret);
-
}
-
-
}
-
-
-
int main(void)
-
{
-
QUE_H my_head;
-
int ret = 0;
-
pthread_t tid[MAX_TID];
-
int tid_num = 0;
-
int i = 0;
-
-
ret = pthread_mutex_init(&my_head.pmut,NULL);
-
debug_return(ret);
-
ret = pthread_cond_init(&my_head.pcon,NULL);
-
debug_return(ret);
-
//一個生產者
-
ret = pthread_create(tid+tid_num,NULL,pro,(void*)&my_head);
-
debug_return(ret);
-
tid_num++;
-
//n個消費者
-
ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
-
debug_return(ret);
-
tid_num++;
-
ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
-
debug_return(ret);
-
tid_num++;
-
ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
-
debug_return(ret);
-
tid_num++;
-
ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
-
debug_return(ret);
-
tid_num++;
-
ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
-
debug_return(ret);
-
-
//堵塞回收
-
for(i = 0;i<=tid_num;i++)
-
{
-
ret = pthread_join( *(tid+i) , NULL );
-
debug_return(ret);
-
}
-
-
ret=pthread_mutex_destroy(&my_head.pmut);
-
debug_return(ret);
-
ret=pthread_cond_destroy(&my_head.pcon);
-
debug_return(ret);
-
- }
作者微信:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2139638/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux多執行緒消費者和生產者模型例項(互斥鎖和條件變數使用)Linux執行緒模型變數
- python中多執行緒消費者生產者問題Python執行緒
- 多執行緒-生產者消費者問題程式碼1執行緒
- 多執行緒之生產者消費者執行緒
- 執行緒同步介紹及 生產者消費者問題舉例 C#版執行緒C#
- linux多執行緒-----同步機制(互斥量、讀寫鎖、條件變數)Linux執行緒變數
- Java多執行緒程式設計(同步、死鎖、生產消費者問題)Java執行緒程式設計
- 多執行緒-生產者消費者問題程式碼2並解決執行緒安全問題執行緒
- java學習回顧---生產者與消費者問題以及多執行緒補充Java執行緒
- Java多執行緒——生產者消費者示例Java執行緒
- JAVA執行緒消費者與生產者模型Java執行緒模型
- linux 生產者與消費者問題Linux
- 生產者消費者模式--java多執行緒同步方法的應用模式Java執行緒
- Java多執行緒——生產者和消費者模式Java執行緒模式
- Java多執行緒14:生產者/消費者模型Java執行緒模型
- Linux Qt使用POSIX多執行緒條件變數、互斥鎖(量)LinuxQT執行緒變數
- linux多執行緒-----同步物件(互斥量、讀寫鎖、條件變數)的屬性Linux執行緒物件變數
- 關於Java多執行緒實現生產者和消費者的問題Java執行緒
- java多執行緒:執行緒間通訊——生產者消費者模型Java執行緒模型
- Java 多執行緒學習(執行緒通訊——消費者和生產者)Java執行緒
- java多執行緒總結六:經典生產者消費者問題實現Java執行緒
- 面試必問:訊號量與生產者消費者問題!面試
- 有名訊號量實現消費者生產者問題
- Java 多執行緒基礎(十二)生產者與消費者Java執行緒
- Java多執行緒——消費者與生產者的關係Java執行緒
- Python-多執行緒及生產者與消費者Python執行緒
- python執行緒通訊與生產者消費者模式Python執行緒模式
- 多執行緒下的生產者和消費者-BlockingQueue執行緒BloC
- java實現生產者消費者問題Java
- 直觀理解生產者消費者問題
- 多執行緒(2)-執行緒同步條件變數執行緒變數
- python多執行緒+生產者和消費者模型+queue使用Python執行緒模型
- 執行緒間的協作(2)——生產者與消費者模式執行緒模式
- 多執行緒併發如何高效實現生產者/消費者?執行緒
- C#多執行緒學習(三) 生產者和消費者C#執行緒
- 用Python多執行緒實現生產者消費者模式Python執行緒模式
- python 多執行緒實現生產者與消費者模型Python執行緒模型
- 多執行緒-生產者消費者之等待喚醒機制執行緒