多線程模型:生產者和消費者

如果僅使用用pthread_mutex,爲了檢查條件是否滿足,需要重複鎖定、檢查、解鎖,這個過程消耗了大量的CPU時間片。

while(true) {
    pthread_mutex_lock(&mutex);
    if (condition)   break;
    pthread_mutex_unlock(&mutex);
}
do_critical();
pthread_mutex_unlock(&mutex);

當然我們可以在檢查變量的時候睡眠幾秒鐘,但這樣一旦在條件滿足的時候就無法作出快速的反應。我們真正需要的是這樣一種過程:在條件不滿足時,線程進入睡眠狀態,一旦條件滿足,線程就會被喚醒。

pthread_cond_wait 和 pthread_cond_signal / pthread_cond_broadcast 正滿足這種情況,使用它們最常見的一種方式就是生產者-消費者模型。

void producer() {
    pthread_mutex_lock(&res_lock);
    pthread_cond_signal(&res_cond); // pthread_cond_broadcast
    Produce(&RES);  // ..(a)
    pthread_mutex_unlock(&res_lock);
}

void consumer()
    pthread_mutex_lock(&res_lock);
    while (RES.count == 0) {
        pthread_cond_wait(&res_cond, &res_lock);
    }
    Consume(&RES);
    pthread_mutex_unlock(&res_lock);
}

producer()中,在調用pthread_cond_singal後consumer()被喚醒,但由於res_lock仍被producer鎖定,consumer會等待在這個互斥變量上,直到producer釋放它。pthread_cond_singal可以在pthread_mutex_lock和pthread_mutex_unlock之間的任何地方,這是由於res_lock被鎖定,任何其他線程都無法訪問到RES,當然也包括剛被喚醒的consumer。pthread_cond_singal也可以在pthread_mutex_unlock之後 (注1),producer早就不在操作RES,其他線程可以開始重新競爭。

cosumer()中,在pthread_mutex_lock和pthread_cond_wait之間,以及pthread_cond_wait和pthread_mutex_unlock之間,res_lock都被該線程鎖定。前一部分就是檢查資源是否可用,當沒有資源時就等待在res_cond上,同時釋放res_lock。這裏釋放res_lock的結果可能會使得其他的consumer進入,但毫無疑問,它們也會等待在res_cond上;也可能使得producer進入,產生資源後喚醒等待在res_cond上的consumer ,或者使得等待在res_lock上的線程進入 (注2)。可能有多個consumer被同時喚醒,試圖從pthread_cond_wait返回,這個返回包含了對res_lock的鎖定,也就是說,只有一個consumer可以鎖定res_lock,從pthread_cond_wait返回,其他的consumer有等待在res_lock上。從pthread_cond_wait返回的consumer再次檢查資源是否可用,由於它是被producer喚醒的第一個線程,資源可用,這個consumer會進入第二部分。第二部分很簡單,就是消耗資源,res_lock在這裏是被鎖定的。第一個consumer退出之後,可能有第二個consumer獲得了res_lock(鎖定了它),這個consumer和前者一樣,檢查資源是否可用。要麼有資源,消耗它,再釋放res_lock;要麼沒有資源,繼續等待在res_cond上,同時也釋放res_lock,要麼讓其他的consumer空歡喜一場,要麼讓producer來解救飢餓的consumer。

注1和注2:producer()中調用pthread_cond_signal在pthread_mutex_unlock之前還是之後是有一點區別的,前者所有的consumer和producer會等待在res_lock上,一起開始競爭(但實際上由於具體實現的關係,也可能有先後次序),後者只有剛開始執行的producer和consumer,以及準備從pthread_cond_wait返回的consumer(它們發現剛纔資源可用,但還需要再檢查資源),它們一起開始競爭,而那些發現資源條件不滿足的consumer會等到被pthread_cond_signal之後才獲得執行的機會。所以爲了公平以及一致(所有的東西都等待在res_lock上),建議pthread_cond_signal放在pthread_mutex_unlock之前。

生產者-消費者模型可以實際應用(實際上它們本身就是一個實際的模型)在桌面版的訪病毒軟件中,把producer當成一個搜索文件並放置文件路徑隊列的過程,把consumer作爲實際掃描病毒的過程,consumer之間讀取文件到內存的操作和掃描文件內容(在內存中)的操作可以有效地使用計算機資源。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章