Linux系統編程—條件變量

條件變量是用來等待線程而不是上鎖的,條件變量一般和互斥鎖一塊兒使用。條件變量之因此要和互斥鎖一塊兒使用,主要是由於互斥鎖的一個明顯的特色就是它只有兩種狀態:鎖定和非鎖定,而條件變量能夠經過容許線程阻塞和等待另外一個線程發送信號來彌補互斥鎖的不足,因此互斥鎖和條件變量一般一塊兒使用。程序員

當條件知足的時候,線程一般解鎖並等待該條件發生變化,一旦另外一個線程修改了環境變量,就會通知相應的環境變量喚醒一個或者多個被這個條件變量阻塞的線程。這些被喚醒的線程將從新上鎖,並測試條件是否知足。通常來講條件變量被用於線程間的同步;當條件不知足的時候,容許其中的一個執行流掛起和等待。面試

簡而言之,條件變量自己不是鎖,但它也能夠形成線程阻塞,一般與互斥鎖配合使用,給多線程提供一個會合的場所。多線程

條件變量的優勢:架構

相較於mutex而言,條件變量能夠減小競爭。若是僅僅是mutex,那麼,無論共享資源裏有沒數據,生產者及全部消費都全一窩蜂的去搶鎖,會形成資源的浪費。函數

如直接使用mutex,除了生產者、消費者之間要競爭互斥量之外,消費者之間也須要競爭互斥量,但若是匯聚(鏈表)中沒有數據,消費者之間競爭互斥鎖是無心義的。有了條件變量機制之後,只有生產者完成生產,纔會引發消費者之間的競爭。提升了程序效率。學習

主要應用函數:測試

pthread_cond_init函數
pthread_cond_destroy函數
pthread_cond_wait函數
pthread_cond_timedwait函數
pthread_cond_signal函數
pthread_cond_broadcast函數網站

以上6 個函數的返回值都是:成功返回0, 失敗直接返回錯誤號。人工智能

pthread_cond_t類型:用於定義條件變量,好比:pthread_cond_t cond;spa

pthread_cond_init函數

函數原型:

int pthread_cond_init(pthread_cond_t restrict cond, const pthread_condattr_t restrict attr);

函數做用:

初始化一個條件變量

參數說明:

cond:條件變量,調用時應傳&cond給該函數

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 restrict cond, pthread_mutex_t restrict mutex);

函數做用:

阻塞等待一個條件變量。具體而言有如下三個做用:

  1. 阻塞等待條件變量cond(參1)知足;
  2. 釋放已掌握的互斥鎖mutex(解鎖互斥量)至關於pthread_mutex_unlock(&mutex);
  3. 當被喚醒,pthread_cond_wait函數返回時,解除阻塞並從新申請獲取互斥鎖

其中一、2.兩步爲一個原子操做。

pthread_cond_timedwait函數

函數原型:

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

函數做用:

限時等待一個條件變量

參數說明:

前兩個比較好理解,重點說明第三個參數。

這裏有個struct timespec結構體,能夠在man sem_timedwait中查看。結構體原型以下:

struct timespec {

​ time_t tv_sec; / seconds / 秒

​ long tv_nsec; / nanosecondes/ 納秒

}

struct timespec定義的形參abstime是個絕對時間。注意,是絕對時間,不是相對時間。什麼是絕對時間?2018年10月1日10:10:00,這就是一個絕對時間。什麼是相對時間?給洗衣機定時30分鐘洗衣服,就是一個相對時間,也就是說從當時時間開始計算30分鐘,諸如此類。

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

adstime所相對的時間是相對於1970年1月1日00:00:00,也就是UNIX計時元年。

下面給出一個錯誤用法:
struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t);
這種用法只能定時到 1970年1月1日 00:00:01秒,想必這個時間你們都還沒出生。

正確用法:
time_t cur = time(NULL); 獲取當前時間。
struct timespec t; 定義timespec 結構體變量t
t.tv_sec = cur+1; 定時1秒
pthread_cond_timedwait (&cond, &mutex, &t); 傳參

pthread_cond_signal函數

函數原型:
int pthread_cond_signal(pthread_cond_t *cond);

函數做用:
喚醒至少一個阻塞在條件變量上的線程

pthread_cond_broadcast函數

函數原型:
int pthread_cond_broadcast(pthread_cond_t *cond);

函數做用:
喚醒所有阻塞在條件變量上的線程

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

不論是什麼語言,只要提到線程同步,一個典型的案例就是生產者消費者模型。在Linux環境下,藉助條件變量來實現這一模型,是比較常見的一種方法。

假定有兩個線程,一個模擬生產者行爲,一個模擬消費者行爲。兩個線程同時操做一個共享資源(通常稱之爲匯聚),生產向其中添加產品,消費者從中消費掉產品。

看以下示例,使用條件變量模擬生產者、消費者問題:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

typedef struct msg {
    struct msg *next;
    int num;
}msg_t;

msg_t *head = NULL;
msg_t *mp = NULL;

/* 靜態初始化 一個條件變量 和 一個互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;

void *th_producer(void *arg)
{
    while (1) {
        mp = malloc(sizeof(msg_t));
        mp->num = rand() % 1000;        //模擬生產一個產品
        printf("--- produce: %d --------\n", mp->num);

        pthread_mutex_lock(&mutex);
        mp->next = head;
        head = mp;
        pthread_mutex_unlock(&mutex);

        pthread_cond_signal(&has_product);      //喚醒線程去消費產品
        sleep(rand() % 5);
    }
    return NULL;
}

void *th_consumer(void *arg)
{
    while (1) {
        pthread_mutex_lock(&mutex);
        while (head == NULL) {      //若是鏈表裏沒有產品,就沒有搶鎖的必要,一直阻塞等待
            pthread_cond_wait(&has_product, &mutex);
        }
        mp = head;
        head = mp->next;        //模擬消費掉一個產品
        pthread_mutex_unlock(&mutex);

        printf("========= consume: %d ======\n", mp->num);
        free(mp);
        mp = NULL;
        sleep(rand() % 5);
    }
    return NULL;
}

int main()
{
    pthread_t pid, cid;
    srand(time(NULL));

    pthread_create(&pid, NULL, th_producer, NULL);
    pthread_create(&cid, NULL, th_consumer, NULL);

    pthread_join(pid, NULL);
    pthread_join(cid, NULL);
    return 0;
}

運行結果:

更多精彩內容,請關注公衆號良許Linux,公衆內回覆1024可免費得到5T技術資料,包括:Linux,C/C++,Python,樹莓派,嵌入式,Java,人工智能,等等。公衆號內回覆進羣,邀請您進高手如雲技術交流羣。

img


最後,最近不少小夥伴找我要Linux學習路線圖,因而我根據本身的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。不管你是面試仍是自我提高,相信都會對你有幫助!

免費送給你們,只求你們金指給我點個贊!

電子書 | Linux開發學習路線圖

也但願有小夥伴能加入我,把這份電子書作得更完美!

有收穫?但願老鐵們來個三連擊,給更多的人看到這篇文章

推薦閱讀:

相關文章
相關標籤/搜索