Linux系統編程---17(條件變量及其函數,生產者消費者條件變量模型,生產者與消費者模型(線程安全隊列),條件變量優點,信號量及其主要函數,信號量與條件變量的區別,)

條件變量

條件變量本身不是鎖!但它也可以造成線程阻塞。通常與互斥鎖配合使用。給多線程提供一個會合的場所。

主要應用函數:

  1. pthread_cond_init 函數
  2. pthread_cond_destroy 函數
  3. pthread_cond_wait 函數
  4. pthread_cond_timedwait 函數
  5. pthread_cond_signal 函數
  6. pthread_cond_broadcast 函數
  7. 以上 6 個函數的返回值都是:成功返回 0, 失敗直接返回錯誤號。
  8. pthread_cond_t 類型 用于定義條件變量
  9. pthread_cond_tcond;

pthread_cond_init 函數

初始化一個條件變量
int pthread_cond_init(pthread_cond_t *restrictcond,const pthread_condattr_t *restrictattr);
參 2:attr 表條件變量屬性,通常為默認值,傳 NULL 即可
也可以使用靜態初始化的方法,初始化條件變量:

pthread_cond_t	cond=PTHREAD_COND_INITIALIZER;

pthread_cond_destroy 函數

銷毀一個條件變量

int	pthread_cond_destroy(pthread_cond_t		*cond);

pthread_cond_wait 函數

阻塞等待一個條件變量

int	pthread_cond_wait(pthread_cond_t	*restrictcond,pthread_mutex_t	*restrictmutex);
函數作用:
  1. 阻塞等待條件變量 cond(參 1)滿足
  2. 釋放已掌握的互斥鎖(解鎖互斥量)相當于 pthread_mutex_unlock(&mutex);
  3. 1.2.兩步為一個原子操作。
  4. 當被喚醒,pthread_cond_wait 函數返回時,解除阻塞并重新申請獲取互斥鎖 pthread_mutex_lock(&mutex);

pthread_cond_timedwait 函數

限時等待一個條件變量

int pthread_cond_timedwait(pthread_cond_t	 *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrictabstime);

參 3: 參看 mansem_timedwait 函數,查看 struct timespec 結構體。

struct	timespec{time_t	tv_sec; /*seconds*/ 秒long tv_nsec; /*nanosecondes*/ 納秒 } 

形參 abstime:絕對時間。
如:time(NULL)返回的就是絕對時間。
而 alarm(1)是相對時間,相對當前時間定時 1 秒鐘。

struct	timespect={1,0}; 
pthread_cond_timedwait(&cond,&mutex,&t); 只能定時到 1970 年 1 月 1 日 00:00:01 秒(早已經過去) 

正確用法:

time_tcur=time(NULL); 獲取當前時間。structtimespect; 定義 timespec 結構體變量 tt.tv_sec=cur+1; 定時 1 秒pthread_cond_timedwait(&cond,&mutex,&t); 傳參 

setitimer 函數還有另外一種時間類型:

 struct	timeval{time_t tv_sec; /*seconds*/ 秒 suseconds_ttv_usec; /*microseconds*/ 微秒};

pthread_cond_signal 函數

喚醒至少一個阻塞在條件變量上的線程

int	pthread_cond_signal(pthread_cond_t*cond);

pthread_cond_broadcast 函數

喚醒全部阻塞在條件變量上的線程

int	pthread_cond_broadcast(pthread_cond_t*cond);

生產者消費者條件變量模型

線程同步典型的案例即為生產者消費者模型,而借助條件變量來實現這一模型,是比較常見的一種方法。假定 有兩個線程,一個模擬生產者行為,一個模擬消費者行為。兩個線程同時操作一個共享資源(一般稱之為匯聚), 生產向其中添加產品,消費者從中消費掉產品。

在這里插入圖片描述

/*借助條件變量模擬  生產者--消費者問題*/
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdio.h>
#include<string.h>
/*鏈表作為共享數據,需要被互斥量保護*/
struct msg{struct msg *next;int num;
};struct msg *head;
struct msg *mp;/*靜態初始化  一個條件變量 和一個互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{for(;;){pthread_mutex_lock(&lock);  //頭指針為空,說明沒有結點 while(head == NULL){pthread_cond_wait(&has_product,&lock); //判斷條件變量是否滿足}mp = head;head = mp->next;        //模擬消費掉一個產品pthread_mutex_unlock(&lock);printf("---Consume ---%d\n",mp->num);free(mp);sleep(rand() %  5);}
}
void *producer()
{for(;;){mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;    //模擬生產一個產品printf("--Produce ---%d\n",mp->num);pthread_mutex_lock(&lock);mp->next=head;              //頭插法head = mp;pthread_mutex_unlock(&lock); //釋放pthread_cond_signal(&has_product); //將等待在該條件變量上的一個線程>喚醒sleep(rand() % 5);}
}int main(int argc,char *argv[])
{pthread_t pid,cid;  //pid生產者ID  cid消費者IDsrand(time(NULL));pthread_create(&pid,NULL,producer,NULL); //生產者pthread_create(&cid,NULL,consumer,NULL); //消費者pthread_join(pid,NULL);pthread_join(cid,NULL);return 0;
}   

在這里插入圖片描述

條件變量是搭配互斥鎖一起使用的
  1. 因為條件變量實現同步只提供等待與喚醒功能,并沒有提供條件判斷的功能,因此條件判斷需要用戶實現,但是條件的操作是一個臨界資源的操作,因此需要受保護,需要在條件判斷之前加鎖
  2. 如果加鎖成功后,因為條件不滿足而陷入休眠,就會導致卡死(因為另一方因為無法獲取鎖,而導致無法促使條件滿足),因此需要在休眠之前解鎖;并且解鎖與休眠必須是原子操作
  3. 被喚醒之后,即將對臨界資源進行操作,但是被操作前還要進行保護加鎖
  4. 所以pthread_cond_wait集合了三步原子操作:解鎖–>等待–>被喚醒后加鎖

條件變量的優點

  1. 相較于 mutex 而言,條件變量可以減少競爭。
  2. 如直接使用 mutex,除了生產者、消費者之間要競爭互斥量以外,消費者之間也需要競爭互斥量,但如果匯聚 (鏈表)中沒有數據,消費者之間競爭互斥鎖是無意義的。有了條件變量機制以后,只有生產者完成生產,才會引 起消費者之間的競爭。提高了程序效率。

生產者與消費者模型(線程安全隊列)

一個場所,兩種角色,三種關系

功能:

  1. 解耦和(兩個關系之間緊密)
  2. 支持忙閑不均
  3. 支持并發

三者關系

生產者–生產者:互斥
消費者–消費者:互斥
生產者–消費者:同步+互斥

在這里插入圖片描述

/*生產者與消費者模型隊列實現                                                                                                                                                                                     * 1.實現線程安全的隊列,對外提供線程安全的數據入隊和出隊操作* 2.創建線程,分別作為生產者與消費者數據入隊或數據出隊*/#include<iostream>
#include<queue>
#include<pthread.h>#define MAX_QUEUE 10
class BlockQueue
{public:BlockQueue(int cap = MAX_QUEUE):_capacity(cap){//初始化隊列pthread_mutex_init(&_mutex,NULL);pthread_cond_init(&_cond_con,NULL);pthread_cond_init(&_cond_pro,NULL);}   ~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond_con);pthread_cond_destroy(&_cond_pro);}   //入隊void QueuePush(int data){QueueLock();while(QueueIsFull()){ //隊列滿了ProWait();    //生產者等待}   _queue.push(data);ConWakeUp();QueueUnLock();}   void QueuePop(int *data){QueueLock();while(QueueIsEmpty()){ConWait();}   *data = _queue.front();//獲取隊列頭結點_queue.pop();//結點出隊ProWakeUp();QueueUnLock();}private://隊列加鎖void QueueLock(){pthread_mutex_lock(&_mutex);}//隊列解鎖void QueueUnLock(){pthread_mutex_unlock(&_mutex);}//消費者等待void ConWait(){pthread_cond_wait(&_cond_con,&_mutex);}//消費者喚醒void ConWakeUp(){pthread_cond_signal(&_cond_con);}//生產者等待void ProWait(){pthread_cond_wait(&_cond_pro,&_mutex);}//生產者喚醒void ProWakeUp(){pthread_cond_signal(&_cond_pro);}//判斷隊列是否為空bool QueueIsFull(){return (_capacity == _queue.size());}//隊列是否是滿的bool QueueIsEmpty(){return _queue.empty();}private:std::queue<int>_queue;//創建隊列int _capacity;//隊列結點最大數量                          //線程安全實現成員pthread_mutex_t _mutex;pthread_cond_t _cond_pro;pthread_cond_t _cond_con;
};void *thr_consumer(void *arg){BlockQueue *q = (BlockQueue *)arg;while(1){int data;q->QueuePop(&data);std::cout<<"consumer"<<pthread_self() <<" get data:"<< data <<std::endl;}return NULL;
}int i = 0; //必須受保護
pthread_mutex_t mutex;void *thr_productor(void *arg){BlockQueue *q = (BlockQueue *)arg;while(1){pthread_mutex_lock(&mutex);q->QueuePush(i++);pthread_mutex_unlock(&mutex);std::cout<<"productor:" <<pthread_self() <<"put data:"<< i <<std::endl;}return NULL;
}int main(int argc,char *argv[])
{BlockQueue q;pthread_t ctid[4],ptid[4];int i,ret;pthread_mutex_init(&mutex,NULL);for(i = 0;i < 4; i++){        ret = pthread_create(&ctid[i],NULL,thr_consumer,(void *)&q);if(ret != 0){std::cout<<"pthread create error\n";return -1;}}for(i = 0;i < 4; i++){ret = pthread_create(&ptid[i],NULL,thr_productor,(void *)&q);if(ret != 0){std::cout<<"pthread create error\n";return -1;}}for(i = 0;i < 4; i++){pthread_join(ctid[i],NULL);} for(i = 0; i < 4;i++){pthread_join(ptid[i],NULL);}return 0;
}

在這里插入圖片描述

信號量

進化版的互斥鎖(1–>N)
由于互斥鎖的粒度比較大,如果我們希望在多個線程間對某一對象的部分數據進行共享,使用互斥鎖是沒有辦 法實現的,只能將整個數據對象鎖住。這樣雖然達到了多線程操作共享數據時保證數據正確性的目的,卻無形中導 致線程的并發性下降。線程從并行執行,變成了串行執行。與直接使用單進程無異。
信號量,是相對折中的一種處理方式,既能保證同步,數據不混亂,又能提高線程并發。

計數器+等待隊列+等待與喚醒功能
  1. 通過自身的計數器實現條件判斷,當前條件滿足時則直接返回并且計數-1.當條件并不滿足時則阻塞
  2. 當產生資源后,通過信號量的喚醒功能喚醒等待并且計數+1

信號量和條件變量實現同步的區別

  1. 信號量的條件判斷由自身來完成,而條件變量的條件判斷由用戶完成
  2. 信號量并不搭配互斥鎖使用,而條件變量需要搭配互斥鎖一起使用保護條件的改變

sem_init 函數

初始化一個信號量
int sem_init(sem_t *sem,int pshared,unsigned int value);
參 1:sem 信號量
參 2:pshared 取 0 用于線程間;取非 0(一般為 1)用于進程間
參 3:value 指定信號量初值

sem_destroy 函數

銷毀一個信號量

int	sem_destroy(sem_t	*sem);

sem_wait 函數

給信號量加鎖 ,對計數進行判斷,計數<=0則阻塞;否則立即返回流程繼續,計數-1

int	sem_wait(sem_t	*sem);

sem_post 函數

給信號量解鎖 ,對計數進行+1,并且喚醒等到的線程

 int	sem_post(sem_t	*sem);

sem_trywait 函數

嘗試對信號量加鎖
(與 sem_wait 的區別類比 lock 和 trylock)

 int	sem_trywait(sem_t	*sem);

sem_timedwait 函數

限時嘗試對信號量加鎖

int	sem_timedwait(sem_t	*sem,const	struct	timespec	*abs_timeout); 

參 2:abs_timeout 采用的是絕對時間。
定時 1 秒:

time_tcur=time(NULL); 獲取當前時間。 
structtimespect; 定義 timespec 結構體變量 t 
t.tv_sec=cur+1; 定時 1 秒 
t.tv_nsec=t.tv_sec+100; 
sem_timedwait(&sem,&t); 傳參

使用信號量實現生產者與消費者模型

在這里插入圖片描述

/*使用信號量實現生產者與消費者模型**/#include<iostream>
#include<queue>
#include<pthread.h>
#include<semaphore.h>class RingQueue
{public:RingQueue(int cap = 10):_capacity(cap),_queue(cap){//1.信號量變量//2.參數取值    0:用于線程間同步與互斥//              非0:用于進程間同步與互斥//3.信號量初值sem_init(&_sem_lock,0,1);//互斥鎖初始值只給1sem_init(&_sem_data,0,0);//初始數據資源數據為0sem_init(&_sem_space,0,cap);//初始空閑空間計數}   ~RingQueue(){sem_destroy(&_sem_lock);sem_destroy(&_sem_data);sem_destroy(&_sem_space);}   void QueuePush(int data){// ProWait();//空閑空間計數判斷是否有空閑空間,若有返回,否則等待// 因為已經通過_sem_space的空閑空間計數知道是否有空閑空間sem_wait(&_sem_space);//添加數據之后,空閑空間計數-1sem_wait(&_sem_lock);//鎖計數初始為1,一旦進入-1加鎖_queue[_step_write]=data;                                                                                                                                                                            _step_write = ( _step_write + 1) % _capacity;sem_post(&_sem_lock);//數據添加完畢后解鎖,數據資源計數+1sem_post(&_sem_data);//數據添加完畢后,數據資源計數+1//ConWakeUp();}   void QueuePop(int *data){sem_wait(&_sem_data);//取數據的時候,數據資源計數-1sem_wait(&_sem_lock);//鎖最好僅僅保護臨界區*data = _queue[_step_read];_step_read = (_step_read + 1) % _capacity;sem_post(&_sem_lock);sem_post(&_sem_space);//取數據之后,空閑空間計數+!}private:std::vector<int>_queue;int _capacity; //隊列最大數量int _step_write;//當前寫到哪里的下標int _step_read;//當前讀到哪里了的下標sem_t _sem_lock;//實現互斥鎖sem_t _sem_space;//空閑空間計數sem_t _sem_data;//數據資源計數/*//隊列加鎖void QueueLock(){pthread_mutex_lock(&_mutex);}//隊列解鎖void QueueUnLock(){pthread_mutex_unlock(&_mutex);}*/  
};
void *thr_productor(void *arg){                                                                                                                                                                                  RingQueue *q = (RingQueue*)arg;int i=0;while(1){q->QueuePush(i);std::cout<<"thread:"<<pthread_self()<<"put data"<<i++<<"\n";}return NULL;
}void *thr_consumer(void *arg){RingQueue *q = (RingQueue*)arg;while(1){int data;q->QueuePop(&data);std::cout<<"thread:"<<pthread_self()<<"get data"<<data<<"\n";}return NULL;
}int main(int argc,char *argv[])
{RingQueue q;pthread_t ptid,ctid[4];int i ,ret;ret = pthread_create(&ptid,NULL,thr_productor,(void *)&q);if(ret != 0){std::cout<<"thread create error\n";return -1;}for(i = 0;i < 4;i++){ret = pthread_create(&ctid[i],NULL,thr_consumer,(void *)&q);if(ret != 0){std::cout<<"thread create error\n";return -1;}}for(i = 0; i < 4; i++){pthread_join(ctid[i],NULL);}pthread_join(ptid,NULL);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383218.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383218.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383218.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

好友

http://blog.csdn.net/liangyuannao/article/details/8583139

Linux系統編程---18(線程池相關概念及其實現)

線程池 概念&#xff1a; 一堆線程任務隊列 作用 避免大量線程頻繁的創建/銷毀時間成本避免瞬間大量線程創建耗盡資源&#xff0c;程序崩潰危險 實現 創建固定數量的線程創建一個線程安全的任務隊列 一種線程使用模式。 線程過多會帶來調度開銷&#xff0c;進而影響緩…

設計模式--1(設計模式基礎,設計模式基本原則,設計模式分類)

設計模式基礎 模式 在一定環境中解決某一問題的方案&#xff0c;包括三個基本元素–問題&#xff0c;解決方案和環境。大白話&#xff1a;在一定環境下&#xff0c;用固定套路解決問題。 設計模式 是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使…

source insight 使用技巧

source insight 使用技巧 1 sourceinsight screen font 的默認字體是Verdana的&#xff0c;它是一直變寬字體。在Document style中可以將字體改為定寬的Courier2 document options->auto indent 去掉indent Open Brace和Indent Close Brace的效果: 繼上一段&#xff0c;在…

設計模式----2(簡單工廠模式的概念,簡單工廠模式的實現,簡單工廠模式的優缺點)

簡單工廠模式 簡單工廠模式的概念 簡單工廠模式屬于類的創建型模式,又叫做靜態工廠方法模式。通過專門定義一個類來負 責創建其他類的實例&#xff0c;被創建的實例通常都具有共同的父類。 具體分類 工廠&#xff08;Creator&#xff09;角色 簡單工廠模式的核心&#xff0…

Redis常見問題及其一些重點知識總結

1、什么是 Redis&#xff1f;簡述它的優缺點&#xff1f; Redis 的全稱是&#xff1a;Remote Dictionary.Server&#xff0c;本質上是一個 Key-Value 類型的內存數據庫&#xff0c;很像 memcached&#xff0c;整個數據庫統統加載在內存當中進行操作&#xff0c;定期通過異步操…

shell生成隨機文件名

1 #!/bin/bash 2 # tempfile-name.sh: 臨時文件名產生器 3 4 BASE_STRmcookie # 32-字符的 magic cookie. 5 POS11 # 字符串中隨便的一個位置. 6 LEN5 # 取得 $LEN 長度連續的字符串. 7 8 prefixtemp # 最終的一個臨時文…

設計模式---3(工廠方法模式的概念,工廠方法模式的實現,工廠方法模式和簡單工廠模式比較)

工廠方法模式 概念 工廠方法模式同樣屬于類的創建型模式又被稱為多態工廠模式 。 工廠方法模式的意義 定義一個創建產品對象的工廠接口&#xff0c;將實際創建工作推遲到子類當中。 核心工廠類不再負責產品的創建&#xff0c;這樣核心類成為一個抽象工廠角色&#xff0c;僅…

設計模式---4(抽象工廠模式的概念,產品組和產品等級的概念,抽象工廠模式的實現)

抽象工廠模式 抽象工廠模式的概念 抽象工廠模式是所有形態的工廠模式中最為抽象和最其一般性的。抽象工廠模式可以向 客戶端提供一個接口&#xff0c;使得客戶端在不必指定產品的具體類型的情況下&#xff0c;能夠創建多個產品 族的產品對象。 抽象工廠的角色及其職責 抽象工…

Win32項目關于MessageBox參數的詳細說明

函數功能&#xff1a;該函數創建、顯示、和操作一個消息框。消息框含有應用程序定義的消息和標題&#xff0c;加上預定義圖標與Push&#xff08;下按&#xff09;按鈕的任何組合。 函數原型&#xff1a;int MessageBox(HWND hWnd,LPCTSTR IpCaption,UINT…

w3af解析

1. w3af簡介 w3afis a Web Application Attack and Audit Framework.即Web應用攻擊和審計框架。w3af用python編寫&#xff0c;依賴的庫主要有2類&#xff0c;分別如下&#xff1a; <1> Core requirements: Python 2.6 fpconst-0.7.2&#xff1a;用于處理IEEE 754浮點…

1.c++中初始化列表和構造函數初始化的區別是什么?2.類的成員變量的初始化順序是按照聲明順序嗎?

初始化列表和構造函數初始化的區別是什么&#xff1f; 初始化和賦值對內置類型的成員沒有太大的區別&#xff0c;在成員初始化列表和構造函數體內進行&#xff0c;在性能和結果上都是一樣的。只有一些需要注意的事項 初始化列表一般情況如下&#xff1a; Date(int year, int …

設計模式---5(建造者模式的概念及其實現,建造者模式的角色與職責,建造者模式和工廠模式的區別)

建造者模式 建造者模式的概念 Builder 模式也叫建造者模式或者生成器模式&#xff0c;是由 GoF 提出的 23 種設計模式中的一種。 Builder 模式是一種對象創建型模式之一&#xff0c;用來隱藏復合對象的創建過程&#xff0c;它把復合對象的 創建過程加以抽象&#xff0c;通過子…

system阻塞SIGCHLD信號原因

system阻塞SIGCHLD信號原因 標簽&#xff1a; c 2014-11-08 11:58 198人閱讀 評論(0) 收藏 舉報 分類&#xff1a; linux編程&#xff08;1&#xff09; 代碼1&#xff1a;APUE10.18節的system函數源代碼 int system(const char *cmdstring) /* with appropriate signal ha…

設計模式6---(單例模式的概念及其實現(懶漢式和餓漢式),線程安全)

單例模式 單例模式的概念 單例模式是一種對象創建型模式&#xff0c;使用單例模式&#xff0c;可以保證為一個類只生成唯一的實例對象。也就是說&#xff0c;在整個程序空間中&#xff0c;該類只存在一個實例對象。 GoF 對單例模式的定義是&#xff1a;保證一個類、只有一個實…

C語言解析http請求表單內容

[1].[文件] cgi.h ~ 405B 下載(105) 跳至 [1] [2] [3] [4] [5] [6] [7] [8] ?123456789101112131415161718192021222324252627#ifndef CGI_H#define CGI_H#include <stdio.h>#include <string.h>#include <stdlib.h>typedef struct Node{char *…

centos給用戶添加sudo權限

linux給用戶添加sudo權限&#xff1a; 有時候&#xff0c;linux下面運行sudo命令&#xff0c;會提示類似&#xff1a; xxxis not in the sudoers file. This incident will be reported. 這里&#xff0c;xxx是用戶名稱&#xff0c;然后導致無法執行sudo命令&#xff0c;這時候…

php手冊

http://www.php100.com/manual/php/ http://www.kuqin.com/php5_doc/

套接字編程---2(TCP套接字編程的流程,TCP套接字編程中的接口函數,TCP套接字的實現,TCP套接字出現的問題,TCP套接字多進程版本,TCP套接字多線程版本)

TCP模型創建流程圖 TCP套接字編程中的接口 socket 函數 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol); domain: AF_INET 這是大多數用來產生socket的協議&#xff0c;使用TCP或UDP來傳輸&…

安全關注

http://yttitan.blog.51cto.com/70821/1576365