多線程編程的原則以及Sem訊號量和Mutex互斥鎖的區別 (一)

來源:互聯網
上載者:User

1. 儘可能的使用mutex理念去設計架構
2. 容易發生的問題
   A. 迴圈死結(互斥鎖)
   B. 非遞迴死結
   C. 資料不同步
   D. 過於積極的記憶體回收,記憶體清理
3. 幾個原則
   A. 操作儘可能私人化
   B. Public方法是在任何Thread、任何時刻可以調用的
   C. 儘可能的使用mutex方式,而非訊號量機制
   D. 必須要對潛在的假設進行判斷,是否符合實際條件?
   E. 獲得鎖之後,儘可能的少操作,以避免在get()內部繼續調用資源鎖

4.Sem與Mutex處理上有何優劣

   找時間總結了一下,如下表:(難免有不足,還請多多指教!)

            Sem             Mutex
Sem功能強大,定位問題比較困難 Mutex功能機制比較穩定,容易定位問題
對資源集區的共用進行保護 對單一資源進行控制
處理序間通訊、線程間通訊 線程間通訊
多線程多任務 同步 多線程多任務 互斥
可以進行一些計算或者資料處理 肯定是鎖住某一資源

5.Sem與Mutex的實現機制

        在Linux下,訊號量和線程互斥鎖的實現都是通過futex系統調用。

        futex(快速使用者區互斥的簡稱)是一個在Linux上實現鎖定和構建進階抽象鎖如訊號量和POSIX互斥的基本工具。它們第一次出現在核心開發的2.5.7版;其語義在2.5.40固定下來,然後在2.6.x系列穩定版核心中出現。

        Futex 是fast userspace mutex的縮寫,意思是快速使用者空間互斥體。Linux核心把它們作為快速的使用者空間的鎖和訊號量的預製構件提供給開發人員。Futex非常基礎,藉助其自身的優異效能,構建更進階別的鎖的抽象,如POSIX互斥體。大多數程式員並不需要直接使用Futex,它一般用來實現像NPTL這樣的系統庫。

        Futex 由一塊能夠被多個進程共用的記憶體空間(一個對齊後的整型變數)組成;這個整型變數的值能夠通過組合語言調用CPU提供的原子操作指令來增加或減少,並且一個進程可以等待直到那個值變成正數。Futex 的操作幾乎全部在應用程式空間完成;只有當操作結果不一致從而需要仲裁時,才需要進入作業系統核心空間執行。這種機制允許使用 futex 的鎖定原語有非常高的執行效率:由於絕大多數的操作並不需要在多個進程之間進行仲裁,所以絕大多數操作都可以在應用程式空間執行,而不需要使用(相對高代價的)核心系統調用。

        futex儲存在使用者空間的共用記憶體中,並且通過原子操作進行操作。在大部分情況下,資源不存在爭用的情況下,進程或者線程可以立刻獲得資源成功,實際上就沒有必要調用系統調用,陷入核心了。實際上,futex的作用就在於減少系統調用的次數,來提高系統的效能。6.Sem與Mutex的實現原理

        線程互斥鎖pthread_mutex_t的實現原理:

atomic_dec(pthread_mutex_t.value);<br />if(pthread_mutex_t.value!=0)<br /> futex(WAIT)<br />else<br /> success </p><p>pthread_mutex_unlock:<br />atomic_inc(pthread_mutex_t.value);<br />if(pthread_mutex_t.value!=1)<br /> futex(WAKEUP)<br />else<br /> success

        訊號量sem_t的實現原理:

sem_wait(sem_t *sem)<br />{<br /> for (;;) { </p><p> if (atomic_decrement_if_positive(sem->count))<br /> break; </p><p> futex_wait(&sem->count, 0)<br /> }<br />} </p><p>sem_post(sem_t *sem)<br />{<br /> n = atomic_increment(sem->count);<br /> // Pass the new value of sem->count<br /> futex_wake(&sem->count, n + 1);<br />}

對比,pthread_mutex_unlock()和sem_post()的實現,我們發現一個不同點,sem_post()無論如何都會調用futex_wake(),進行系統調用。但是pthread_mutex_unlock()卻符合futex的初衷,只有在需要仲裁的時候才調用futex_wake()。那麼什麼是仲裁條件呢?

前面說過訊號量和線程互斥鎖語義上的區別在於訊號量的value>=0,而線程互斥鎖的value可以為負數。
        對於lock操作,這兩個倒是沒有多少差別。訊號量只要value>0就可以獲得資源,線程互斥鎖需要value=1。
       但是對於unlock操作,這兩個就有一些差別了。訊號量和線程互斥鎖,都會增加對應的value。如果加1後,value為1,對於線程互斥鎖來講,實際上表明資源可用,並且之前沒有其他的線程在等待這個資源;否則說明還有其他線程在等待這個資源,需要調用futex系統調用喚醒它們。但是對於訊號量,由於value必須>=0。那麼加1後,即使value為1,也無法判定現在沒有其他的進程或線程正在等待資源,所以必須調用futex系統調用。例如:

#include <stdio.h><br />#include <semaphore.h><br />#include <pthread.h> </p><p>sem_t sem_a;<br />void *task1(); </p><p>int main(void)<br />{<br /> int ret=0;<br /> pthread_t thrd1;<br /> pthread_t thrd2;<br /> sem_init(&sem_a,0,1);<br /> ret=pthread_create(&thrd1,NULL,task1,NULL); //建立子線程<br /> ret=pthread_create(&thrd2,NULL,task1,NULL); //建立子線程<br /> pthread_join(thrd1,NULL); //等待子線程結束<br /> pthread_join(thrd2,NULL); //等待子線程結束<br />} </p><p>void *task1()<br />{<br /> int sval = 0;<br /> sem_wait(&sem_a); //持有訊號量<br /> sleep(5); //do_nothing<br /> sem_getvalue(&sem_a,&sval);<br /> printf("sem value = %d/n",sval);<br /> sem_post(&sem_a); //釋放訊號量<br />}

上面sem的value初始化為1,但是有兩個線程爭用資源。那麼第一個線程獲得資源成功,當它unlock的時候,sem的value變為1。但是,這個時候,實際上還有一個線程在等待資源。因此,必須要進行futex_wake()系統調用,喚醒等待資源的線程。

 

本文參考了以下部落格的內容,在此表示感謝!

    http://www.eetop.cn/blog/html/04/343504-14125.html

    http://dev.firnow.com/course/6_system/linux/Linuxjs/20090901/173322.html

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.