【Linux】條件變量與信號量

場景
    服務器有多個線程要寫文件,若是一塊兒寫的話,就會發生順序混亂,有可能剛剛寫好了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 
版權聲明:本文爲博主原創文章,轉載請附上博文連接!函數

相關文章
相關標籤/搜索