線程同步(互斥鎖與訊號量的作用與區別)

來源:互聯網
上載者:User

“訊號量用在多線程多任務同步的,一個線程完成了某一個動作就通過訊號量告訴別的線程,別的線程再進行某些動作(大家都在semtake的時候,就阻塞在 哪裡)。而互斥鎖是用在多線程多任務互斥的,一個線程佔用了某一個資源,那麼別的線程就無法訪問,直到這個線程unlock,其他的線程才開始可以利用這 個資源。比如對全域變數的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和訊號量會同時使用的”
也就是說,訊號量不一定是鎖定某一個資源,而是流程上的概念,比如:有A,B兩個線程,B線程要等A線程完成某一任務以後再進行自己下面的步驟,這個任務 並不一定是鎖定某一資源,還可以是進行一些計算或者資料處理之類。而線程互斥量則是“鎖住某一資源”的概念,在鎖定期間內,其他線程無法對被保護的資料進 行操作。在有些情況下兩者可以互換。

兩者之間的區別:

範圍
訊號量: 進程間或線程間(linux僅線程間的無名訊號量pthread semaphore)
互斥鎖: 線程間

上鎖時 
訊號量: 只要訊號量的value大於0,其他線程就可以sem_wait成功,成功後訊號量的value減一。若value值不大於0,則sem_wait使得線程阻塞,直到sem_post釋放後value值加一,但是sem_wait返回之前還是會將此value值減一
互斥鎖: 只要被鎖住,其他任何線程都不可以訪問被保護的資源

以下是號誌(量)的一些概念:

號誌與互斥鎖和條件變數的主要不同在於”燈”的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說後兩中同步方式側重於”等待”操作,即資 源停用話,號誌機制則側重於點燈,即告知資源可用;
沒有等待線程的解鎖或激發條件都是沒有意義的,而沒有等待燈亮的線程的點燈操作則有效,且能保持 燈亮狀態。當然,這樣的操作原語也意味著更多的開銷。

號誌的應用除了燈亮/燈滅這種二元燈以外,也可以採用大於1的燈數,以表示資源數大於1,這時可以稱之為多元燈。

1. 建立和 登出

POSIX號誌標準定義了有名號誌和無名號誌兩種,但LinuxThreads的實現僅有無名燈,同時有名燈除了總是可用於多進程之間以外,在使用上與無名燈並沒有很大的區別,因此下面僅就無名燈進行討論。

int sem_init(sem_t *sem, int pshared, unsigned int value)
這是建立號誌的API,其中value為號誌的初值,pshared表示是否為多進程共用而不僅僅是用於一個進程。LinuxThreads沒有實現 多進程共用號誌,因此所有非0值的pshared輸入都將使sem_init()返回-1,且置errno為ENOSYS。初始化好的號誌由sem變 量表徵,用於以下點燈、滅燈操作。

int sem_destroy(sem_t * sem)
被登出的號誌sem要求已沒有線程在等待該號誌,否則返回-1,且置errno為EBUSY。除此之外,LinuxThreads的號誌 登出函數不做其他動作。
sem_destroy destroys a semaphore object, freeing the resources it  might  hold.  No  threads  should  be  waiting  on  the
       semaphore  at  the  time  sem_destroy  is  called.  In  the  LinuxThreads implementation, no resources are associated with
       semaphore objects, thus sem_destroy actually does nothing except checking that no thread is waiting on the semaphore.

2. 點燈和滅燈

int sem_post(sem_t * sem)

點燈操作將號誌值原子地加1,表示增加一個可訪問的資源。

int sem_wait(sem_t * sem)
int sem_trywait(sem_t * sem)

sem_wait()為等待燈亮操作,等待燈亮(號誌值大於0),然後將號誌原子地減1,並返回。sem_trywait()為sem_wait()的非阻塞版,如果號誌計數大於0,則原子地減1並返回0,否則立即返回-1,errno置為EAGAIN。

3. 擷取燈值

int sem_getvalue(sem_t * sem, int * sval)

讀取sem中的燈計數,存於*sval中,並返回0。

4. 其他

sem_wait()被實現為取消點。(取消點事什麼意思。。。)
sem_wait is a cancellation point.
取消點的含義:
當用pthread_cancel()一個線程時,這個要求會被pending起來,當被cancel的線程走到下一個cancellation point時,線程才會被真正cancel掉。
而且在支援原子”比較且交換CAS”指令的體繫結構上,sem_post()是唯一能用於非同步訊號處理函數的POSIX非同步訊號 安全的API。

On processors supporting atomic compare-and-swap (Intel 486, Pentium and later, Alpha, PowerPC, MIPS  II,  Motorola  68k),
       the  sem_post function is async-signal safe and can therefore be called from signal handlers. This is the only thread syn-
       chronization function provided by POSIX threads that is async-signal safe.

       On the Intel 386 and the Sparc, the current LinuxThreads implementation of sem_post is not async-signal safe  by  lack  of
       the required atomic operations.

互斥量(Mutex)

 

互斥量表現互斥現象的資料結構,也被當作二元號誌。一個互斥基本上是一個多任務敏感的二元訊號,它能用作同步多任務的行為,它常用作保護從中斷來的臨界段代碼並且在共用同步使用的資源。

 

Mutex本質上說就是一把鎖,提供對資源的獨佔訪問,所以Mutex主要的作用是用於互斥。Mutex對象的值,只有0和1兩個值。這兩個值也分別代表了Mutex的兩種狀態。值為0, 表示鎖定狀態,當前對象被鎖定,使用者進程/線程如果試圖Lock臨界資源,則進入排隊等待;值為1,表示空閑狀態,當前對象為空白閑,使用者進程/線程可以Lock臨界資源,之後Mutex值減1變為0。

Mutex可以被抽象為四個操作:

- 建立 Create

- 加鎖 Lock

- 解鎖 Unlock

- 銷毀 Destroy

Mutex被建立時可以有初始值,表示Mutex被建立後,是鎖定狀態還是空閑狀態。在同一個線程中,為了防止死結,系統不允許連續兩次對Mutex加鎖(系統一般會在第二次調用立刻返回)。也就是說,加鎖和解鎖這兩個對應的操作,需要在同一個線程中完成。

不同作業系統中提供的Mutex函數:

動作\系統

Win32

Linyx

Solaris

建立

CreateMutex

pthread_mutex_init

mutex_init

加鎖

WaitForSingleObject

pthread_mutex_lock

mutex_lock

解鎖

ReleaseMutex

pthread_mutex_unlock

mutex_unlock

銷毀

CloseHandle

pthread_mutex_destroy

mutex_destroy

 

訊號量

訊號量(Semaphore),有時被稱為號誌,是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們能夠正確、合理的使用公用資源。

訊號量可以分為幾類:

² 二進位訊號量(binary semaphore):只允許訊號量取0或1值,其同時只能被一個線程擷取。

² 整型訊號量(integer semaphore):訊號量取值是整數,它可以被多個線程同時獲得,直到訊號量的值變為0。

² 記錄型訊號量(record semaphore):每個訊號量s除一個整數值value(計數)外,還有一個等待隊列List,其中是阻塞在該訊號量的各個線程的標識。當訊號量被釋放一個,值被加一後,系統自動從等待隊列中喚醒一個等待中的線程,讓其獲得訊號量,同時訊號量再減一。

訊號量通過一個計數器控制對共用資源的訪問,訊號量的值是一個非負整數,所有通過它的線程都會將該整數減一。如果計數器大於0,則訪問被允許,計數器減1;如果為0,則訪問被禁止,所有試圖通過它的線程都將處於等待狀態。

計數器計算的結果是允許訪問共用資源的通行證。因此,為了訪問共用資源,線程必須從訊號量得到通行證, 如果該訊號量的計數大於0,則此線程獲得一個通行證,這將導致訊號量的計數遞減,否則,此線程將阻塞直到獲得一個通行證為止。當此線程不再需要訪問共用資源時,它釋放該通行證,這導致訊號量的計數遞增,如果另一個線程等待通行證,則那個線程將在那時獲得通行證。

  Semaphore可以被抽象為五個操作:

- 建立 Create

- 等待 Wait:

線程等待訊號量,如果值大於0,則獲得,值減一;如果只等於0,則一直線程進入睡眠狀態,知道訊號量值大於0或者逾時。

-釋放 Post

執行釋放訊號量,則值加一;如果此時有正在等待的線程,則喚醒該線程。

-試圖等待 TryWait

如果調用TryWait,線程並不真正的去獲得訊號量,還是檢查訊號量是否能夠被獲得,如果訊號量值大於0,則TryWait返回成功;否則返回失敗。

-銷毀 Destroy

訊號量,是可以用來保護兩個或多個關鍵程式碼片段,這些關鍵程式碼片段不能並發調用。在進入一個關鍵程式碼片段之前,線程必須擷取一個訊號量。如果關鍵程式碼片段中沒有任何線程,那麼線程會立即進入該框圖中的那個部分。一旦該關鍵程式碼片段完成了,那麼該線程必須釋放訊號量。其它想進入該關鍵程式碼片段的線程必須等待直到第一個線程釋放訊號量。為了完成這個過程,需要建立一個訊號量,然後將Acquire Semaphore VI以及Release Semaphore VI分別放置在每個關鍵程式碼片段的首末端。確認這些訊號量VI引用的是初始建立的訊號量。

動作\系統

Win32

POSIX

建立

CreateSemaphore

sem_init

等待

WaitForSingleObject

sem _wait

釋放

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.