閱讀了unix網絡編程的卷二以後,看着裏面的實例而且理解其原理算法,就將裏面的C語言的鎖API進行C++封裝以供之後使用。實現的鎖接口以及一些算法會封裝到個人TimePass庫中。我以爲應該就鎖的問題寫一個系列的博客。鎖按照其做用域能夠分爲線程鎖和進程鎖; 按照其工做方式:又能夠分爲互斥鎖,讀寫鎖,文件鎖。讀寫鎖也是互斥,只是相對於讀寫鎖來講更加精細,其分爲讀和寫,讀與讀不會互斥,讀和寫會互斥,寫與寫也會互斥。文件鎖有相對於讀寫鎖來講更加精細了,對整個文件,能夠分區段進行加鎖。html
Table of Contents
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 }
這是生產線程,在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 }
等待生產者通知之因此放到while裏面,是爲了防止consume_wait調用失敗,形成飢餓消費.
以上是用互斥鎖實現生產者和消費者的思路。在這裏爲了方便使用,我實現了一個四個類,類圖以下:
以上這個UML圖,是用linux下面的Dia畫,其徹底能夠替代visio,並且支持將UML圖片轉化爲各類文件,好比png。
這個生產者和消費者的框架使用起來很簡單,只須要實現一個類來繼承ProduceConsume,實現其中的純虛函數。而後啓用線程,調用ThreadFuncLoader裏面的Produce和Consume就OK了,使用者不用糾結鎖的問題。
在這裏使用者所要實現的函數有:
Produce對應上面的begin_produce
Consume對應上面的begin_consume
ProduceComplete對應上面的「生產結束」
ConsumeComplete對應上面你的「生產線上沒有了數據"
Condition 對應上面的「通知條件」「等待條件」