歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux管理 >> Linux網絡

linux網絡編程之posix 線程(四)

posix 條件變量與互斥鎖 示例生產者--消費者問題

一、posix 條件變量

一種線程間同步的情形:線程A需要等某個條件成立才能繼續往下執行,現在這個條件不成立,線程A就阻塞等待,而線程B在執行過程中使這個條件成立了,就喚醒線程A繼續執行。

在pthread庫中通過條件變量(Condition Variable)來阻塞等待一個條件,或者喚醒等待這個條件的線程。Condition Variable用pthread_cond_t類型的變量表示,和Mutex的初始化和銷毀類似,pthread_cond_init函數初始化一個Condition Variable,attr參數為NULL則表示缺省屬性,pthread_cond_destroy函數銷毀一個Condition Variable。如果ConditionVariable是靜態分配的,也可以用宏定義PTHEAD_COND_INITIALIZER初始化,相當於用pthread_cond_init函數初始化並且attr參數為NULL。

一個Condition Variable總是和一個Mutex搭配使用的。一個線程可以調用pthread_cond_wait在一個Condition Variable上阻塞等待,這個函數做以下三步操作:

1. 釋放Mutex

2. 阻塞等待

3. 當被喚醒時,重新獲得Mutex並返回

注意:3個操作是原子性的操作,之所以一開始要釋放Mutex,是因為需要讓其他線程進入臨界區去更改條件,或者也有其他線程需要進入臨界區等待條件。

一個線程可以調用pthread_cond_signal喚醒在某個Condition Variable上等待的第一個線程,也可以調用pthread_cond_broadcast喚醒在這個Condition Variable上等待的所有線程。

上面的函數具體可以man 一下。

二、條件變量使用規范

(一)、等待條件代碼

pthread_mutex_lock(&mutex);

while (條件為假)

pthread_cond_wait(cond, mutex);

修改條件

pthread_mutex_unlock(&mutex);

(二)、給條件發送通知代碼

pthread_mutex_lock(&mutex);

設置條件為真

pthread_cond_signal(cond);

pthread_mutex_unlock(&mutex);

假設第一段用於消費者線程,第二段用於生產者線程。為什麼消費者線程要用while 而不是if 就可以呢?在man pthread_cond_wait 有一句:If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was not interrupted, or it shall return zero due to spurious wakeup.

即是說如果正在等待條件變量的一個線程收到一個信號,從信號處理函數返回的時候線程會重新等待條件變量就好象沒有被中斷一樣,或者被虛假地喚醒返回0。如果是虛假喚醒的情形,那麼其實條件並未被改變,那麼此時如果沒有繼續判斷一下條件的真假就繼續向下執行的話,修改條件將會出現問題,所以需要使用while 循環再判斷一下,如果條件還是為假必須繼續等待。

當生產者線程較多,即生產得比較快,在這邊假設是無界的緩沖區(比如鏈表),可以不停地生產,使用pthread_cond_signal  發出通知的時候,如果此時沒有消費者線程在等待條件,那麼這個通知將被丟棄,但也不影響整體代碼的執行,沒有消費者線程在等待,說明產品資源充足,即while 判斷失敗,不會進入等待狀態,直接消費產品(即修改條件)。

三、生產者消費者問題

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
    
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
    
#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)
    
#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 1
    
pthread_mutex_t g_mutex;
pthread_cond_t g_cond;
    
pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT];
    
int nready = 0;
    
void *consume(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        while (nready == 0)
        {
            printf("%d begin wait a condtion ...\n", num);
            pthread_cond_wait(&g_cond, &g_mutex);
        }
    
        printf("%d end wait a condtion ...\n", num);
        printf("%d begin consume product ...\n", num);
        --nready;
        printf("%d end consume product ...\n", num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }
    return NULL;
}
    
void *produce(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        printf("%d begin produce product ...\n", num);
        ++nready;
        printf("%d end produce product ...\n", num);
        pthread_cond_signal(&g_cond);
        printf("%d signal ...\n", num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }
    return NULL;
}
    
int main(void)
{
    int i;
    
    pthread_mutex_init(&g_mutex, NULL);
    pthread_cond_init(&g_cond, NULL);
    
    
    for (i = 0; i < CONSUMERS_COUNT; i++)
        pthread_create(&g_thread[i], NULL, consume, (void *)i);
    
    sleep(1);
    
    for (i = 0; i < PRODUCERS_COUNT; i++)
        pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);
    
    for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++)
        pthread_join(g_thread[i], NULL);
    
    pthread_mutex_destroy(&g_mutex);
    pthread_cond_destroy(&g_cond);
    
    return 0;
}

在上面程序中,++nready 就當作是生產產品了,--nready就當作是消費產品了,跟前面文章所不同的是,這裡沒有緩沖區大小的概念,可以當作是無界緩沖區,生產者可以一直生產,但消費者只有當有產品的時候才能消費,否則就得等待,等待結束的條件就是nready > 0;上面也說過當生產得比較快(生產者線程多)的時候,也有可能消費者線程一直不存在等待的狀態,因為nready 的值很大,即產品資源很多。現在設置的是2個消費者線程和1個生產者線程,所以動態輸出來看一般是2個消費者線程輪流等待。

Copyright © Linux教程網 All Rights Reserved