場景
服務器有多個線程要寫文件,若是一塊兒寫的話,就會發生順序混亂,有可能剛剛寫好了ABC還沒來得及更新,就被後面的DEF覆蓋掉。
解決方案
建立多個線程來讀取服務器中的內容寫到緩衝區,再建立一個線程專門讀取緩衝區中的內容,緩衝區存滿了就不能往裏面寫了,等讀端讀取部分數據
後通知寫端能夠往緩衝區中寫入數據了,緩衝區空了就不能讀取數據了,等待寫端寫入數據後通知它能夠讀了。
條件變量
1.定義條件變量 pthread_cond_t cond;
2.初始化 pthread_cond_init(&cond, NULL); // 第二個條件是屬性
3.等待條件 pthread_cond_wait(&cond, &mutex);
// 若是mutex沒有在互斥環境,就形同虛設,在互斥環境中wait函數將其置爲1(1表示打開鎖,無論原來的值是多少),
執行結束後,wait返回,mutex恢復成原來的值。
4.修改條件 pthread_cond_signal(&cond); // 激活等待條件,至關於一個通知
5.銷燬條件變量 pthread_cond_destroy(&cond);
多個生產者往緩衝區中放入東西的時候,頗有可能放到同一塊地方,所以要求生產者之間是互斥關係。同理消費者也是如此。
這就須要加上互斥鎖來保證每次只有一個線程來操做。
生產者
int pthread_mutex_lock(pthread_mutex_t *mutex);
生產
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
消費者
int pthread_mutex_lock(pthread_mutex_t *mutex);
if (沒有可消費)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
else
消費
int pthread_mutex_unlock(pthread_mutex_t *mutex);
分析:若是此時消費者發現沒有能夠消費的,因而就在等待生產者生產,可是此時生產者不能生產(ps:此時生產者因爲加鎖,進不去)。
因而就產生了死鎖。而pthread_cond_wait的第二個參數是個互斥鎖,能夠將生產者的鎖解開。
規範寫法
pthread_mutex_lock();
while (條件不知足)
pthread_cond_wait();
pthread_mutex_unlock();
分析爲何用while:這是由於wait函數是個阻塞函數,頗有可能被信號打斷,而wait函數返回有兩種狀況,一種是被signal函數通知,
一種是被別的信號打斷,設置成while後,若是是被信號打斷,那麼再次進入條件判斷時,仍然是不知足的,不會執行下面的操做,
繼續等待。這種信號打斷的方式也叫做假喚醒。
pthread_mutex_lock();
pthread_cond_signal(); // 若是沒有線程等待,也就是說條件一直知足,那麼信號將被丟棄。
eg:假設條件是大於0,那麼一開始大小爲4時,知足條件,信號就不須要激活條件,執行完一次操做後,變成3仍然知足條件,信號
也會被丟棄。
pthread_mutex_unlock();
生產者-消費者模型
int *p = malloc(sizeof(int));
*p = i;
pthread_create(&tid[i], NULL, producer, p);
在線程中切記要釋放掉開闢的堆內存
int id = *(int *)arg;
free(arg);
信號量 #include <semaphore.h>服務器
1.定義信號量 sem_t sem;
2.初始化信號量 int sem_init(sem_t *sem, int pshared, unsigned int value);
a. sem地址 b. 0表示本進程中多個線程間同步,非0表示能夠跨進程的同步操做。c. 信號量初值(計數器的值)
3.PV操做
P int sem_wait(sem_t *sem); // sem-1 若是小於0就阻塞
V int sem_post(sem_t *sem); // sem+1
4.銷燬 int sem_destroy(sem_t *sem);
倉庫存放東西有限
生產者
while (1)
{
pthread_mutex_lock();
sem_wait(sem_full);
生產
sem_post(sem_empty);
pthread_mutex_unlock();
}
消費者
while (1)
{
pthread_mutex_lock();
sem_wait(sem_empty);
消費
sem_post(sem_full);
pthread_mutex_unlock();
}
場景:http服務器是基於短鏈接的,那麼在網頁上的訪問就會頻繁地建立和銷燬線程,這樣開銷是很大的。
線程池(生產者消費者模型)
根據系統性能建立合適大小的線程池。
將建立的線程存入在線程池中,須要線程時,就讓它們創建聯繫,不想用了,就斷開聯繫。
1.線程池中有若干個線程
2.用於執行大量相對短暫的任務
計算密集型任務:大量的時間佔用CPU進行運算。
線程池中線程的個數等於CPU個數,避免線程切換。
IO密集型任務:大量的時間佔用CPU在阻塞等待IO。
線程池中線程個數大於CPU個數。
當任務增長時,能夠動態增長線程池中線程的個數。當任務執行完成後,能夠動態減小線程池中線程個數。
需求:
生產者線程向任務隊列中添加任務,任務隊列中有任務,若是線程池中有等待線程就喚醒它並執行任務,若是線程池中沒有
等待線程,而且沒有達到上限,就添加新的線程到線程池。
沒有任務執行就等待,有任務則去執行。
typedef struct condition
{
pthread_mutex_t mutex;
pthread_cond_t cond;
}condition_t;
// 任務隊列
typedef struct task
{
void *(pfun)(void *); // 任務隊列的回調函數(管理任務的)
void *arg; // 回調函數傳入的參數
struct task *next;
}task_t;
typedef struct threadpool
{
condition_t cond; // 同步與互斥
task_t *first; // 任務隊列的隊頭
task_t *tail; // 任務隊列的隊尾
int max_thread; // 最大線程個數
int idle; // 空閒線程個數 若是有空閒線程,此時能夠signal通知下
int counter; // 當前線程池中的線程個數
int quit; // 若是爲1表示退出,爲0表示不退出
}threadpool_t;
// 初始化
void threadpool_init(threadpool_t *pool, int max);
// 往線程池添加任務
void threadpool_add(threadpool_t *pool, void*(*pf)(void*), void *arg);
// 銷燬線程池
void threadpool_destroy(threadpool_t *pool);
pthread_cond_broadcast(); // 喚醒全部
pthread_cond_signal(); // 只喚醒一個
---------------------
做者:sustzc
來源:CSDN
原文:https://blog.csdn.net/sustzc/article/details/82734994
版權聲明:本文爲博主原創文章,轉載請附上博文連接!函數