Unix/linux進程及線程間同步技術總結【學習總結,請勿吐槽。。。】


  為允許在線程或進程間共享數據,同步通常是必須的。常見的同步方式有:互斥鎖、條件變量、讀寫鎖、信號量。另外,對於進程間的同步,也可以通過進程間通信的方式進行同步,包括管道(無名管道、有名管道)、信號量、消息隊列、共享內存、遠程過程調用,當然也可以通過Socket來進行網絡控制。

一.  互斥鎖和條件變量是同步的基本組成部分

  互斥鎖和條件變量出自Posix.1線程標准,多用來同步一個進程中各個線程。但如果將二者存放在多個進程間共享的內存區中,它們也可以用來進行進程間的同步。

1. 互斥鎖用於保護臨界區,以保護任何時刻只有一個線程在執行其中的代碼,其大體輪廓大體如下:

  lock_the_mutex(...);

  臨界區

  unlock_the_mutex(...);

  下列三個函數給一個互斥鎖上鎖和解鎖:

  #include <pthread.h>

  int pthread_mutex_lock(pthread_mutex_t *mptr);  //若不能立刻獲得鎖,將阻塞在此處

  int pthread_mutex_trylock(pthread_mutex_t *mptr);  //若不能立刻獲得鎖,將返回EBUSY,用戶可以根據此返回值做其他操作,非阻塞模式

  int pthread_mutex_unlock(pthread_mutex_t *mptr);  //釋放鎖

  互斥鎖通常用於保護由多個線程或多個進程分享的共享數據(Share Data)

2.  條件變量,它是發送信號與等待信號。互斥鎖用戶上鎖,條件變量則用於等待。一般來說,在一個進程/線程中調用pthread_cond_wait(..)等待某個條件的成立,此時該進程阻塞在這里,另外一個進程/線程進行某種操作,當某種條件成立時,調用pthread_cond_signal(...)來發送信號,從而使pthread_cond_wait(...)返回。此處要注意的是,這里所談到的信號,不是系統級別的SIGXXXX信號,只是用信號這個詞語更容易理解。條件變量與信號量更接近或者就可以認為是信號量。

  下列兩個函數用來對條件變量進行控制:

  #include <pthread.h>

  int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);

  int pthread_cond_signal(pthread_cond_t *cptr);

  由代碼我們可以看出,條件變量的使用是需要結合鎖機制的,即上面所提到的互斥鎖。也就是說,一個進程/線程要等到臨界區的共享數據達到某種狀態時再進行某種操作,而這個狀態的成立,則是由另外一個進程/線程來完成后發送信號來通知的。

  其實想一想,pthread_cond_wait函數也可以用一個while死循環來等待條件的成立,但要注意的是,使用while死循環會嚴重消耗CPU,而pthread_cond_wait則是采用線程睡眠的方式,它是一種等待模式,而不是一直的檢查模式。

  總的來說,給條件變量發送信號的代碼大體如下:

  pthread_mutex_lock(&mutex);

  設置條件為真

  pthread_cond_signal(&cond);  //發送信號

  pthread_mutex_unlock(&mutex);  

  等待條件並進入睡眠以等待條件變為真的代碼大體如下:

  pthread_mutex_lock(&mutex); 

  while(條件為假)

    pthread_cond_wait(&cond,&mutex);  

  執行某種操作

  pthread_mutex_unlock(&mutex);

  在這里需要注意的是,pthread_cond_wait(&cond,&mutex)是一個院子操作,當它執行時,首先對mutex解鎖,這樣另外的線程才能得到鎖來修改條件,pthread_cond_wait解鎖后,再將本身的線程/進程投入睡眠,另外,當該函數返回時,會再對mutex進行加鎖,這樣才能“執行某種操作”后unlock鎖。


二、 讀寫鎖

  顧名思義,讀寫鎖也是一種鎖,他是在互斥鎖的基礎上進行了改進,當一個進程/線程獲得寫入鎖時,其他的進程/線程仍然可以獲得鎖,只不過獲得的是讀取鎖,因為一個進程/線程寫入,不影響其他進程/線程的讀操作。

 

三、 信號量

  英文:semaphore,它是一種專門用於提供不同進程間或線程間同步手段的原語。可以通過下圖來理解它。

        進程A                               進程B

          \          /

     進程    \         /

        ----------------------------------------------------

     內核     \      /

              信號量

  也就是說,信號量是由內核來維護的,他獨立出進程。因此可以通過它來進行同步。

  上圖一般來說,是基於Posix有名信號量,可以認為它是系統中的一個特殊文件(因為在Linux中,一切都可以認為是文件),因為在進程間的通信、同步中用的比較多,如果是線程之間的同步,經常用基於Posix內存的信號量。(基於內存的信號量必須在創建時指定是否在進程間共享,有名信號量隨內核有持續性,需手工刪除,而基於內存的信號量具有隨進程的持續性)

  對於信號量的工作原理,其實和互斥鎖+條件變量相似。

  主要函數有:sem_open、sem_close、sem_unlink,這里要注意,close只是關閉信號量,但並未從系統中刪除,而unlink是刪除該信號量。

  sem_wait和sem_trywait函數,他們和pthread_cond_wait功能相似,都是等待某個條件的成立,sem_wait和sem_trywait的區別是,當所指定的信號量的值為0時,后者並不將調用者投入睡眠,而是立刻返回EAGAIN,即重試。

  sem_post和sem_getvalue函數,sem_post將指定的信號量加一,然后喚醒正在等待該信號量值變為正數的任意線程。sem_getvalue是用來獲取當前信號量值的函數。

 

總結:

  互斥鎖、條件變量、信號量三者的差別:

  (1) 互斥鎖必須總是由給他上鎖的線程解鎖(因為此時其他線程根本得不到此鎖),信號量沒有這種限制:一個線程等待某個信號量,而另一個線程可以掛出該信號量

  (2)每個信號量有一個與之關聯的值,掛出時+1,等待時-1,那么任何線程都可以掛出一個信號,即使沒有線程在等待該信號量的值。不過對於條件變量來說,如果pthread_cond_signal之后沒有任何線程阻塞在pthread_cond_wait上,那么此條件變量上的信號丟失。

  (3)在各種各樣的同步技巧中,能夠從信號處理程序中安全調用的唯一函數是sem_post


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM