1. 程式人生 > >Linux下執行緒間通訊及同步

Linux下執行緒間通訊及同步

1、Linux “執行緒”

程序與執行緒之間是有區別的,不過Linux核心只提供了輕量程序的支援,未實現執行緒模型。Linux是一種“多程序單執行緒”的作業系統。Linux本身只有程序的概念,而其所謂的“執行緒”本質上在核心裡仍然是程序。

大家知道,程序是資源分配的單位,同一程序中的多個執行緒共享該程序的資源(如作為共享記憶體的全域性變數)。Linux中所謂的“執行緒”只是在被建立時clone了父程序的資源,因此clone出來的程序表現為“執行緒”,這一點一定要弄清楚。因此,Linux“執行緒”這個概念只有在打冒號的情況下才是最準確的。

目前Linux中最流行的執行緒機制為LinuxThreads,所採用的就是執行緒-程序“一對一”模型,排程交給核心,而在使用者級實現一個包括訊號處理在內的執行緒管理機制。LinuxThreads由Xavier Leroy (

[email protected])負責開發完成,並已繫結在GLIBC中發行,它實現了一種BiCapitalized面向Linux的Posix 1003.1c “pthread”標準介面。Linuxthread可以支援Intel、Alpha、MIPS等平臺上的多處理器系統。

按照POSIX 1003.1c 標準編寫的程式與Linuxthread 庫相連結即可支援Linux平臺上的多執行緒,在程式中需包含標頭檔案pthread. h,在編譯連結時使用命令:

gcc -D -REENTRANT -lpthread xxx. c

其中-REENTRANT巨集使得相關庫函式(如stdio.h、errno.h中函式) 是可重入的、執行緒安全的(thread-safe),-lpthread則意味著連結庫目錄下的libpthread.a或libpthread.so檔案。使用Linuxthread庫需要2.0以上版本的Linux核心及相應版本的C庫(libc 5.2.18、libc 5.4.12、libc 6)。

2、“執行緒”控制

(1)執行緒建立

程序被建立時,系統會為其建立一個主執行緒,而要在程序中建立新的執行緒,則可以呼叫pthread_create:

pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void*), void *arg);

start_routine為新執行緒的入口函式,arg為傳遞給start_routine的引數。

每個執行緒都有自己的執行緒ID,以便在程序內區分。執行緒ID在pthread_create呼叫時回返給建立執行緒的呼叫者;一個執行緒也可以在建立後使用pthread_self()呼叫獲取自己的執行緒ID:pthread_self (void) ;

(2)執行緒退出

執行緒的退出方式有三種方式:

a、執行完成後隱式退出;

b、由執行緒本身顯示呼叫pthread_exit 函式退出;pthread_exit (void * retval) ;

c、被其他執行緒用pthread_cancel函式終止:pthread_cancel (pthread_t thread) ;

在某執行緒中呼叫此函式,可以終止由引數thread 指定的執行緒。

如果一個執行緒要等待另一個執行緒的終止,可以使用pthread_join函式,該函式的作用是呼叫pthread_join的執行緒將被掛起直到執行緒ID為引數thread的執行緒終止:

pthread_join (pthread_t thread, void** threadreturn);

3、.執行緒通訊

(1)執行緒互斥

互斥意味著“排它”,即兩個執行緒不能同時進入被互斥保護的程式碼。Linux下可以通過pthread_mutex_t 定義互斥體機制完成多執行緒的互斥操作,該機制的作用是對某個需要互斥的部分,在進入時先得到互斥體,如果沒有得到互斥體,表明互斥部分被其它執行緒擁有,此時欲獲取互斥體的執行緒阻塞,直到擁有該互斥體的執行緒完成互斥部分的操作為止。

下面的程式碼實現了對共享全域性變數x 用互斥體mutex 進行保護的目的:

int x; // 程序中的全域性變數
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按預設的屬性初始化互斥體變數mutex
pthread_mutex_lock(&mutex); // 給互斥體變數加鎖
… //對變數x 的操作
phtread_mutex_unlock(&mutex); // 給互斥體變數解除鎖

(2)執行緒同步

同步就是執行緒等待某個事件的發生。只有當等待的事件發生執行緒才繼續執行,否則執行緒掛起並放棄處理器。當多個執行緒協作時,相互作用的任務必須在一定的條件下同步。

Linux下的C語言程式設計有多種執行緒同步機制,最典型的是條件變數(condition variable)。

a、pthread_cond_init用來建立一個條件變數,其函式原型為:

pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);

b、pthread_cond_wait和pthread_cond_timedwait用來等待條件變數被設定,值得注意的是這兩個等待呼叫需要一個已經上鎖的互斥體mutex,這是為了防止在真正進入等待狀態之前別的執行緒有可能設定該條件變數而產生競爭。pthread_cond_wait的函式原型為:

pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);

c、pthread_cond_broadcast用於設定條件變數,即使得事件發生,這樣等待該事件的執行緒將不再阻塞。pthread_cond_broadcast的函式原型為:

pthread_cond_broadcast (pthread_cond_t *cond) ;

d、pthread_cond_signal則用於解除某一個等待執行緒的阻塞狀態,其函式原型為:

pthread_cond_signal (pthread_cond_t *cond) ; 

e、pthread_cond_destroy 則用於釋放一個條件變數的資源。

在標頭檔案semaphore.h 中定義的訊號量則完成了互斥體和條件變數的封裝,按照多執行緒程式設計中訪問控制機制,控制對資源的同步訪問,提供程式設計人員更方便的呼叫介面。

a、sem_init(sem_t *sem, int pshared, unsigned int val);

這個函式初始化一個訊號量sem 的值為val,引數pshared 是共享屬性控制,表明是否在程序間共享。

b、sem_wait(sem_t *sem);

呼叫該函式時,若sem為無狀態,呼叫執行緒阻塞,等待訊號量sem值增加(post )成為有訊號狀態;若sem為有狀態,呼叫執行緒順序執行,但訊號量的值減一。

c、sem_post(sem_t *sem);

 呼叫該函式,訊號量sem的值增加,可以從無訊號狀態變為有訊號狀態。

4、例項

下面我們還是以有名的生產者/消費者問題為例來闡述Linux執行緒的控制和通訊。一組生產者執行緒與一組消費者執行緒通過緩衝區發生聯絡。生產者執行緒將生產的產品送入緩衝區,消費者執行緒則從中取出產品。緩衝區有N 個,是一個環形的緩衝池。

#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 16 // 緩衝區數量
struct prodcons {
// 緩衝區相關資料結構
    int buffer[BUFFER_SIZE]; /* 實際資料存放的陣列*/
    pthread_mutex_t lock; /* 互斥體lock 用於對緩衝區的互斥操作 */
    int readpos, writepos; /* 讀寫指標*/
    pthread_cond_t notempty; /* 緩衝區非空的條件變數 */
    pthread_cond_t notfull; /* 緩衝區未滿的條件變數 */
};

/* 初始化緩衝區結構 */
void init(struct prodcons *b) {
    pthread_mutex_init(&b->lock, NULL);
    pthread_cond_init(&b->notempty, NULL);
    pthread_cond_init(&b->notfull, NULL);
    b->readpos = 0;
    b->writepos = 0;
}

/* 將產品放入緩衝區,這裡是存入一個整數*/
void put(struct prodcons *b, int data) {
    pthread_mutex_lock(&b->lock);
    /* 等待緩衝區未滿*/
    if ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
        pthread_cond_wait(&b->notfull, &b->lock);
    }

    /* 寫資料,並移動指標 */
    b->buffer[b->writepos] = data;
    b->writepos++;
    if (b->writepos > = BUFFER_SIZE) {
        b->writepos = 0;
    }

    /* 設定緩衝區非空的條件變數*/
    pthread_cond_signal(&b->notempty);
    pthread_mutex_unlock(&b->lock);
}

/* 從緩衝區中取出整數*/
int get(struct prodcons *b) {
    int data;
    pthread_mutex_lock(&b->lock);
    /* 等待緩衝區非空*/
    if (b->writepos == b->readpos) {
        pthread_cond_wait(&b->notempty, &b->lock);
    }

    /* 讀資料,移動讀指標*/
    data = b->buffer[b->readpos];
    b->readpos++;
    if (b->readpos > = BUFFER_SIZE) {
        b->readpos = 0;
    }

    /* 設定緩衝區未滿的條件變數*/
    pthread_cond_signal(&b->notfull);
    pthread_mutex_unlock(&b->lock);
    return data;
}

/* 測試:生產者執行緒將1 到10000 的整數送入緩衝區,消費者執行緒從緩衝區中獲取整數,兩者都列印資訊*/
#define OVER ( - 1)
struct prodcons buffer;
void *producer(void *data) {
    int n;
    for (n = 0; n < 10000; n++) {
        printf("%d --->\n", n);
        put(&buffer, n);
    }
    put(&buffer, OVER);
    return NULL;
}

void *consumer(void *data) {
    int d;
    while (1) {
        d = get(&buffer);
        if (d == OVER) {
            break;
        }
        printf("--->%d \n", d);
    }
    return NULL;
}

int main(void) {
    pthread_t th_a, th_b;
    void *retval;
    init(&buffer);
    /* 建立生產者和消費者執行緒*/
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);

    /* 等待兩個執行緒結束*/
    pthread_join(th_a, &retval);
    pthread_join(th_b, &retval);
    return 0;
}

5、WIN32、VxWorks、Linux執行緒類比

目前為止,筆者已經創作了《基於嵌入式作業系統VxWorks的多工併發程式設計》(《軟體報》2006年5~12期連載)、《深入淺出Win32多執行緒程式設計》(天極網技術專題)系列,我們來找出這兩個系列文章與本文的共通點。

看待技術問題要瞄準其本質,不管是Linux、VxWorks還是WIN32,其涉及到多執行緒的部分都是那些內容,無非就是執行緒控制和執行緒通訊,它們的許多函式只是名稱不同,其實質含義是等價的,下面我們來列個三大作業系統共同點詳細表單:

 6、小結

本章講述了Linux下多執行緒的控制及執行緒間通訊程式設計方法,給出了一個生產者/消費者的例項,並將Linux的多執行緒與WIN32、VxWorks多執行緒進行了類比,總結了一般規律。鑑於多執行緒程式設計已成為開發併發應用程式的主流方法,學好本章的意義也便不言自明。

相關推薦

Linux執行通訊同步

1、Linux “執行緒” 程序與執行緒之間是有區別的,不過Linux核心只提供了輕量程序的支援,未實現執行緒模型。Linux是一種“多程序單執行緒”的作業系統。Linux本身只有程序的概念,而其所謂的“執行緒”本質上在核心裡仍然是程序。 大家知道,程序是資源分配的單位,同

Java執行通訊同步問題、wait/notify使用

Java執行緒間通訊簡單來說就是多個執行緒同時操作同一個資源,比如一個執行緒設定了姓名、性別,另一個執行緒讀取姓名、性別。 伴隨而來的就是同步問題:比如執行緒a先設定了張三、男,後設置了李四、女,執行緒b要列印這些資訊,由於多執行緒的隨機性,a設定張三、男後,然後a又設定了李四,還未設定性別時,

linux執行的建立,同步和退出》

概述         前面有一篇文章專門講述了程序建立,監控和終止,這一篇文章進一步來談談執行緒的建立和同步等操作(這裡指的是POSIX規範下的執行緒,即Pthreads)。和探討程序的文章類似,還是通過講述相關呼叫的使用和注意事項來推進,並提供一些例項來做說明。 第一部

linux c 執行同步通訊)的幾種方法--互斥鎖,條件變數,訊號量,讀寫鎖

轉載自:https://blog.csdn.net/vertor11/article/details/55657619Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。 下面是思維導圖: 一、互斥鎖(mutex)   鎖機制是同一時刻只允

Java多執行程式設計--使用Lock物件實現同步以及執行通訊

前幾篇: 在《Java多執行緒程式設計-(4)-執行緒間通訊機制的介紹與使用》已經學習了,可以使用方法wait/notify 結合同步關鍵字syn

Linux 學習筆記—執行通訊的概念和執行控制函式

1 執行緒間通訊 執行緒間無需特別的手段進行通訊,由於執行緒間能夠共享資料結構,也就是一個全域性變數能夠被兩個執行緒同一時候使用。只是要注意的是執行緒間須要做好同步,一般用mutex。執行緒間的通訊目的主要是用於執行緒同步,所以執行緒沒有像程序通訊中的用於資料交

Java多執行程式設計-(5)-使用Lock物件實現同步以及執行通訊

前幾篇: 在《Java多執行緒程式設計-(4)-執行緒間通訊機制的介紹與使用》已經學習了,可以使用方法wait/notify 結合同步關鍵字syn

執行安全、執行同步執行通訊

一、執行緒安全 多個執行緒在執行同一段程式碼的時候,每次的執行結果和單執行緒執行的結果都是一樣的,不存在執行結果的二義性,就可以稱作是執行緒安全的。 講到執行緒安全問題,其實是指多執行緒環境下對共享資源的訪問可能會引起此共享資源的不一致性。因此,為避免執行緒安全問題,應該避免多執行緒環境下對

Java多執行(二) —— 執行安全、執行同步執行通訊(含面試題集)

上一篇博文:Java多執行緒(一) —— 執行緒的狀態詳解中詳細介紹了執行緒的五種狀態及狀態間的轉換。本文著重介紹了執行緒安全的相關知識點,包括執行緒同步和鎖機制、執行緒間通訊以及相關面試題的總結 一、執行緒安全 多個執行緒在執行同一段程式碼的時候,每次的執行結果和單執行緒執行的結果都是一樣的,不存在執行結果

執行無需特別的手段進行通訊,因為執行可以共享資料結構,也就是一個全域性變數可以被兩個執行同時使用,不過要注意的是執行需要做好同步

     執行緒間無需特別的手段進行通訊,因為執行緒間可以共享資料結構,也就是一個全域性變數可以被兩個執行緒同時使用。不過要注意的是執行緒間需要做好同步,一般用mutex。可以參考一些比較新的UNIX/Linux程式設計的書,都會提到Posix執行緒程式設計,比如《UNIX

Linux執行同步的幾種方法

Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數和訊號量。一、互斥鎖(mutex)   鎖機制是同一時刻只允許一個執行緒執行一個關鍵部分的程式碼。 1. 初始化鎖   int pthread_mutex_init(pthread_mutex_t *m

C++多執行--執行通訊執行同步

原文地址:http://blog.csdn.net/yanpingsz/article/details/5891693(轉)                 http://blog.csdn.net/zjc0888/article/details/7372258 (轉)

Linux執行同步的幾種常見方法

Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數和訊號量。一、互斥鎖(mutex)  鎖機制是同一時刻只允許一個執行緒執行一個關鍵部分的程式碼。 1. 初始化鎖  int pthread_mutex_init(pthread_mutex_t *mutex

linux程序/執行通訊(《unix網路程式設計-程序通訊》讀書筆記)

linux程序間/執行緒間通訊 linux下的程序通訊手段基本上是從Unix平臺上的程序通訊手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟體釋出中心)在程序間通訊方面的側重點有所不同。前者對Unix早

Linux的程序/執行通訊方式總結

Linux系統中的程序間通訊方式主要以下幾種: 同一主機上的程序通訊方式    * UNIX程序間通訊方式: 包括管道(PIPE), 有名管道(FIFO), 和訊號(Signal)    * System V程序通訊方式:包括訊號量(Semaphore), 訊息佇列

java多執行同步以及執行通訊詳解&amp;消費者生產者模式&amp;死鎖&amp;Thread.join()(多執行程式設計之二)

從執行結果,我們就可以看出我們4個售票視窗同時賣出了1號票,這顯然是不合邏輯的,其實這個問題就是我們前面所說的執行緒同步問題。不同的執行緒都對同一個資料進了操作這就容易導致資料錯亂的問題,也就是執行緒不同步。那麼這個問題該怎麼解決呢?在給出解決思路之前我們先來分析一下這個問題是怎麼產生的?我們宣告一個執行緒類

Java多執行程式設計(6)--執行通訊()

  因為本文的內容大部分是以生產者/消費者模式來進行講解和舉例的,所以在開始學習本文介紹的幾種執行緒間的通訊方式之前,我們先來熟悉一下生產者/消費者模式。   在實際的軟體開發過程中,經常會碰到如下場景:某個模組負責產生資料(可能是訊息、檔案、任務等),這些資料由另一個

juc包:使用 juc 包的顯式 Lock 實現執行通訊

# 一、前置知識 執行緒間通訊三要素: 多執行緒+**判斷**+操作+**通知**+資源類。 上面的五個要素,其他三個要素就是普通的多執行緒程式問題,那麼通訊就需要執行緒間的互相通知,往往伴隨著何時通訊的判斷邏輯。 在 java 的 Object 類裡就提供了對應的方法來進行通知,同樣的,保證

34-多執行--死鎖+執行通訊+等待喚醒機制+多生產者多消費者問題

一、死鎖 1、死鎖的常見情形之一:同步的巢狀 說明:同步的巢狀,至少得有兩個鎖,且第一個鎖中有第二個鎖,第二個鎖中有第一個鎖。eg:同步程式碼塊中有同步函式,同步函式中有同步程式碼塊。下面的例子,同步程式碼塊的鎖是obj,同步函式的鎖是this。t1執行緒先執行同步程式碼塊,獲取鎖obj,需

執行通訊的訊息機制的Message和Handler

Message是訊息機制的資訊載體,開發人員可以在Message物件中封裝資料,封裝資料的方式有: 1)setData(),在Message中封裝Bundle型別的資料,在接收方使用getData()獲取該Bundle物件。 2)arg1屬性,int型別,用於封裝int型別變數 3)ar