linux進程同步方式總結__linux
來源:互聯網
上載者:User
1. 上鎖和解鎖 #inlcude<pthread.h> int pthread_mutex_lock(pthread_mutex_t *mptr); int pthread_mutex_trylock(pthread_mutex_t *nptr); int pthread_mutex_unlock(pthread_mutex_t *mptr);
互斥鎖是協作性鎖。這就是說,如果共用資料是一個鏈表,那麼操縱該鏈表的所有線程都必須在實際操縱前擷取該鎖。不過也沒辦法防止某個線程不首先擷取該互斥鎖就操縱該鏈表。 struct{ pthread_mutex_t mutex; int buff[MAX]; int nput; int nval; }shared={PTHREAD_MUTEX_INITIALIZER}; 生產者-消費者問題 多個生產者 vs 單個消費者,我們只考慮生產者之間的同步問題,不考生產者和消費者之間的同步問題,因為我們確保所有的生產者建立完畢才建立消費者。 生產者代碼: void *produce(void *arg)
{ for(;;){ pthread_mutex_lock(&shared.mutex); if(shared.nput>=nitems){ pthread_mutex_unlock(&shared.mutex); return NULL; } shared.buff[shared.nput]=shared.nval; shared.nput++; shared.nval++; pthread_mutex_unlock(&shared.mutex); *((int*)arg)+=1; } }
對比上鎖與等待 生產者和消費者,這裡消費者在生產者建立完成之後立馬建立,所以同步生產者和消費者,消費者不能去取那些未存放的緩衝區,在沒有條件變數的情況下,消費者需要for(;;)輪詢是否有生產者生產產品,這裡我們需要另外一種類型的同步,它允許一個線程(進程)睡眠到某個事件發生--這就是條件變數的功能。
2.條件變數 互斥鎖用於上鎖,條件變數用於等待。這倆種不同類型的同步都是需要的。條件變數的類型是pthread_cond_t的變數。 int pthread_cond_wait(pthread_cond_t *cptr,pthread_mutex_t *mptr); int pthread_cond_signal(pthread_cond_t *cptr); 這倆個函數等待或由之得以通知的“條件”,其定義由我們選擇:我們在代碼中測試這種條件。 每個條件變數都有一個互斥鎖與之關聯。我們調用pthread_cond_wait等待某個條件為真時,還會指定其條件變數的地址和所關聯的互斥鎖地址。 給條件變數發送訊號代碼: struct{ pthread_mutex_t mutex; pthread_cond_t cond; 維護本條件的各個變數 }var={PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,...}; pthread_mutex_lock(&var.mutex); 設定條件為真 pthread_cond_signal(&var.cond);
測試條件並進入睡眠以等待該條件變為真: pthread_mutex_unlock(&var.mutex); while(條件為假) pthread_cond_wait(&var.cond,&var.mutex); 修改條件 pthread_mutex_unlock(&var.mutex);
----------------------------------------------- 調用pthread_cond_wait(&nready.cond,&nread.mutex)的進程會進入睡眠,該函數執行下面倆個動作 (1)給互斥鎖nread.mutex解鎖 (2)把調用線程投入睡眠,直到另外某個線程就本條件變數調用pthread_cond_signal。 pthread_cond_wait在返回前重新給互斥鎖nready.mutex上鎖。 3.讀寫鎖 以後再介紹吧 4.訊號量 訊號量是一種用於提供不同進程或一個給定進程的不同線程間同步手段的原語。 Posix提供倆類訊號量:有名訊號量和基於記憶體的訊號量,後者也稱為無名訊號量。
我們主要研究無名訊號量 (1)int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); 測試指定訊號量的值,如果該值大於0,那就將他減1並立即返回。如果該值等於0,調用線程就被投入睡眠,直到該值變為大於0,這時再將它減1,函數隨後返回。
sem_trywait在訊號量等於0時並不投入睡眠,而是返回一個EAGIN錯誤。 如果被某個訊號中斷,sem_wait也可能過早返回,所返回的錯誤是EINTR。
int sem_getvalue(sem_t *sem,int *valp); 當一個線程使用完某個訊號量,它應該調用sem_post。如果該訊號量當前上鎖,那麼傳回值是0,或為某個負數,其絕對值等待的線程數目。
互斥鎖、條件變數、訊號量區別: 首先:互斥鎖必須總是由給它上鎖的線程解鎖;訊號量沒有這種限制:一個線程可以等待某個訊號量,而另一個線程可以掛出該訊號量。
其次,既然每個訊號量有一個與之關聯的值,它由掛出操作加1,由等待操作減1,那麼任何線程都可以掛出一個訊號(譬如說將他的值由0變為1),即使當時沒有線程在等待該訊號量值變為正數也沒有關係。然而,如果某個線程調用了pthread_cond_signal,不過當時沒有任何線程阻塞在pthread_cond_wait調用中,那麼發往相應條件變數的訊號將丟失。
最後,在各種各樣的同步技巧(互斥鎖、條件變數、讀寫鎖、訊號量)中,能夠從訊號處理常式中安全調用的唯一函數是sem_post。
互斥鎖是為上鎖最佳化的,條件變數是為等待最佳化的,訊號量既可以用於上鎖,也可以用於等待,所以可能導致更多的開銷。
建立無名訊號量: int sem_init(sem_t *sem,int shared,unsigned int value); int sem_destroy(sem_t *sem);
sem:指嚮應用程式必須分配的sem_t變數。 shared:0,那麼初始化的訊號量是在同一進程的各個線程間共用,否則該訊號量是在進程間共用的(包括父子進程,所以父子進程間這個參數也是1),當shared非零時,該訊號量必須放在某種類型的共用記憶體區中,而即使將使用它的所有進程都要能訪問該共用記憶體區。 value:訊號量的初值
當不需要使用與有名訊號量關聯的名字時,可改用基於記憶體的訊號量。彼此無親緣關係的不同進程需要使用訊號量時,通常使用有名訊號量。其名字就是各個進程標識訊號量的手段。 我們在圖1-3中說過,基於記憶體的訊號量至少具有隨進程的持久性,然而他們真正的持久性卻取決於存放訊號量的記憶體區類型。只要含有某個基於記憶體訊號量的記憶體區保持有效,該訊號量就一直存在。
如果某個基於記憶體的訊號量時由單個進程內的各個線程共用(sem_init的shared參數是0),那麼該訊號量具有隨進程的持久性,當該進程終止時它消失。 如果某個基於記憶體的訊號量時在不同進程間共用的(sem_init的shared為1),那麼該訊號量必須放在共用記憶體中,因而只要該共用區仍然存在,該訊號量也就繼續存在。共用記憶體區具有隨核心的持久性,這意味著伺服器可以建立一個共用記憶體區,在該共用記憶體區中初始化一個Posix基於記憶體的訊號量,然後終止。一段時間後,一個或多個客戶可以開啟該共用記憶體區,訪問存放在其中的基於記憶體的訊號量。