unix網絡編程--鎖(一)

  閱讀了unix網絡編程的卷二以後,看着裏面的實例而且理解其原理算法,就將裏面的C語言的鎖API進行C++封裝以供之後使用。實現的鎖接口以及一些算法會封裝到個人TimePass庫中。我以爲應該就鎖的問題寫一個系列的博客。鎖按照其做用域能夠分爲線程鎖和進程鎖; 按照其工做方式:又能夠分爲互斥鎖,讀寫鎖,文件鎖。讀寫鎖也是互斥,只是相對於讀寫鎖來講更加精細,其分爲讀和寫,讀與讀不會互斥,讀和寫會互斥,寫與寫也會互斥。文件鎖有相對於讀寫鎖來講更加精細了,對整個文件,能夠分區段進行加鎖。html

1 互斥鎖的API

  (1) int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr *mutexattr);linux

  初始化mutex; pthreadmutexattrt裏面的shared字段設置成PTHREAD_PROCESS_SHARE,能夠經過在共享內存裏面建立鎖,實現進程間的互斥鎖。git

  (2) int pthread_mutex_destroy(pthread_mutext *mutex);github

  銷燬鎖資源算法

  (3) int pthread_mutex_lock(pthread_mutext *mutex);編程

   獲取鎖的資源,若是已經有進程或者線程獲取到了就阻塞掉網絡

  (4) int pthread_mutex_unlock(pthread_mutext *mutex);多線程

  釋放所資源,在阻塞的進程中或者線程中,按照阻塞的隊列順序獲取鎖資源併發

  (5) int pthread_mutex_trylock(pthread_mutext *mutex);框架

  嘗試獲取所資源,若是恰巧所資源被佔用,就會返回EBUSY的錯誤碼; 其不會像pthread_mutex_lock阻塞掉。這個函數能夠用來查看鎖的佔用情況。

  以上若是僅僅是實現互斥鎖的封裝已經夠用了,互斥鎖的類名爲Mutex,其數據成員以下:

class Mutex {
public:
//成員函數 …. private: pthread_mutex_t mutex_; }

  (7) int pthread_cond_init(pthread_condt *cond, pthread_cond_attrt * condattr);

  初始化條件資源

  (8) int pthread_cond_destroy(pthreadcondt *cond);

  銷燬條件資源

  (9) int pthread_cond_wait(pthreadcondt *cond, pthreadmutext *mutex);

   將mutex與cond進行綁定,當執行wait的時候,首先對mutex進行解鎖,而後再進入阻塞隊列,直到pthreadcondsignal通知,才喚醒; 緊接着佔用鎖資源,才向下執行。

  (10)int pthreadcondsignal(pthreadcondt *cond);

   經過條件資源喚醒互斥鎖

  以上經過這些函數能夠實現條件鎖:

class CondMutex:public Mutex{
public:
//成員函數 …. private: pthread_cond_t cond_; }

2 消費者和生產者的原理

  想象一下,在一條生產線上,多臺機器來生產物品,一臺機器來打包物品;這其中生產線就是一個隊列,生產物品的機器就是生產者,打包物品的機器就是消費者。在計算機算法中,就生產者和消費者而言,生產物品的機器就是生產者,消費物品的機器是消費者。每一個生產者分別生產完一次物品,中止下來,讓消費者去消費物品直到消費者消費完全部物品,停下來;生產者再去繼續生產,而且第一個生產者通知生產線上已經有物品了,讓消費者繼續消費。就這樣不停的往返反覆,生產者和消費者共同去維護一個隊列,在這過程當中,生產者和消費者可以達到併發。

  爲何不是多個生產者和多個消費者,這樣子速度不是更快麼?

  爲了維持隊列的順序,生產者和生產者是互斥的,消費者與消費者是互斥的,惟有生產者和消費不互斥。多個生產者確實有它的優點,當只有一個生產者的時候,生產線上有了一個產品以後,生產線中止,讓消費者去消費,這樣子就徹底沒有達到消費者和生產者的併發,只是串行的。當生產線上的物品都消耗完了,一個消費者會解鎖,而且進入等待隊列,這時候多是另外一個在等待隊列中的消費者獲取到鎖資源,形成這個消費者飢餓消費;其次當生產者生產了物品,去通知消費者消費,要一個一個的去通知,即便採用羣體喚醒,消費者們是互斥的;何況當消費過程是一個簡短的過程,一個線程進行消費和多線程加鎖進行消費比起來並不會慢,反而加鎖和解鎖會形成性能消耗的。注意只有當消費過程是一個比較耗時的過程,纔會考慮有沒有加鎖的必要。

   爲何多個生產者分別生產一次物品,就會停下來?

   畢竟生產者消費者問題並不會像富士康生產線那樣,流水線上兩端的工人步調一致,生產線兩頭的工人不須要互相通知。一個生產者生產玩一次物品以後,會有一個通知的過程,爲了防止多個線程同時通知,形成紊亂,就對通知過程也加鎖了,而這個鎖就是消費用的鎖。當通知過去的時候,消費者會消費完當前物品,直到釋放鎖(消費者消費的過程當中一直加鎖解鎖,能夠在解鎖的時候,sleep一下,同時也在生產者的過程當中sleep一下,能夠防止過分的消費或者過分的生產,儘可能讓消費者和生產者步調一致,不過sleep的時間不容易把握)。

3 消費者和生產者的實現

 1   //生產者線程函數
 2   void* produce (void* arg) {
 3       while (生產沒結束) {
 4           produce_lock()
 5           if (生產結束) {
 6               produce_unlock()
 7               return NULL;
 8           }
 9           //往生產線上放數據
10           begin_produce();
11           produce_unlock();
12           
13           //這裏的鎖運行速度很快
14           consume_lock();
15           if (知足通知條件) {
16               //通知消費者去生產
17               pthread_cond_signal();
18           consume_unlock()
19       }
20       
21   }
View Code

  這是生產線程,在producelock裏面,咱們還須要判斷生產是否結束,是爲了防止這樣的狀況發生,當多個生產者線程被阻塞了,有一個線程完成了全部的生產任務,若是不判斷,其餘的生產線程繼續往下執行,直到這個這次循環結束爲止,會超出生產的量。

 1   //消費者線程函數
 2   void* consume(void* arg) {
 3        while (生產沒結束 || 生產線上還有數據) {
 4            consume_lock();
 5            while (等待條件) { //注意這個條件與上面的通知條件是相反的
 6               //等待生產者的通知
 7               consume_wait();
 8            }
 9            begin_consume()
10            consume_unlock()
11       }
12   }
View Code

  等待生產者通知之因此放到while裏面,是爲了防止consume_wait調用失敗,形成飢餓消費.

  以上是用互斥鎖實現生產者和消費者的思路。在這裏爲了方便使用,我實現了一個四個類,類圖以下:

  

  以上這個UML圖,是用linux下面的Dia畫,其徹底能夠替代visio,並且支持將UML圖片轉化爲各類文件,好比png。

  這個生產者和消費者的框架使用起來很簡單,只須要實現一個類來繼承ProduceConsume,實現其中的純虛函數。而後啓用線程,調用ThreadFuncLoader裏面的Produce和Consume就OK了,使用者不用糾結鎖的問題。

  在這裏使用者所要實現的函數有:

  Produce對應上面的begin_produce

  Consume對應上面的begin_consume

  ProduceComplete對應上面的「生產結束」

  ConsumeComplete對應上面你的「生產線上沒有了數據"

  Condition 對應上面的「通知條件」「等待條件」

相關文章
相關標籤/搜索