閱讀了unix網絡編程的卷二之後,看著裡面的實例並且理解其原理算法,就將裡面的C語言的鎖API進行C++封裝以供以後使用。實現的鎖接口以及一些算法會封裝到我的TimePass庫中。我覺得應該就鎖的問題寫一個系列的博客。鎖按照其作用域可以分為線程鎖和進程鎖; 按照其工作方式:又可以分為互斥鎖,讀寫鎖,文件鎖。讀寫鎖也是互斥,只是相對於讀寫鎖來說更加精細,其分為讀和寫,讀與讀不會互斥,讀和寫會互斥,寫與寫也會互斥。文件鎖有相對於讀寫鎖來說更加精細了,對整個文件,可以分區段進行加鎖。
(1) int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr *mutexattr);
初始化mutex; pthreadmutexattrt裡面的shared字段設置成PTHREAD_PROCESS_SHARE,可以通過在共享內存裡面創建鎖,實現進程間的互斥鎖。
(2) int pthread_mutex_destroy(pthread_mutext *mutex);
銷毀鎖資源
(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,其數據成員如下:
public:
(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);
通過條件資源喚醒互斥鎖
以上通過這些函數可以實現條件鎖:
CondMutex:public:
想象一下,在一條生產線上,多台機器來生產物品,一台機器來打包物品;這其中生產線就是一個隊列,生產物品的機器就是生產者,打包物品的機器就是消費者。在計算機算法中,就生產者和消費者而言,生產物品的機器就是生產者,消費物品的機器是消費者。每個生產者分別生產完一次物品,停止下來,讓消費者去消費物品直到消費者消費完所有物品,停下來;生產者再去繼續生產,並且第一個生產者通知生產線上已經有物品了,讓消費者繼續消費。就這樣不停的往返反復,生產者和消費者共同去維護一個隊列,在這過程中,生產者和消費者能夠達到並發。
為什麼不是多個生產者和多個消費者,這樣子速度不是更快麼?
為了維持隊列的順序,生產者和生產者是互斥的,消費者與消費者是互斥的,唯有生產者和消費不互斥。多個生產者確實有它的優勢,當只有一個生產者的時候,生產線上有了一個產品之後,生產線停止,讓消費者去消費,這樣子就完全沒有達到消費者和生產者的並發,只是串行的。當生產線上的物品都消耗完了,一個消費者會解鎖,並且進入等待隊列,這時候可能是另一個在等待隊列中的消費者獲取到鎖資源,造成這個消費者饑餓消費;其次當生產者生產了物品,去通知消費者消費,要一個一個的去通知,即使采用群體喚醒,消費者們是互斥的;況且當消費過程是一個簡短的過程,一個線程進行消費和多線程加鎖進行消費比起來並不會慢,反而加鎖和解鎖會造成性能消耗的。注意只有當消費過程是一個比較耗時的過程,才會考慮有沒有加鎖的必要。
為什麼多個生產者分別生產一次物品,就會停下來?
畢竟生產者消費者問題並不會像富士康生產線那樣,流水線上兩端的工人步調一致,生產線兩頭的工人不需要互相通知。一個生產者生產玩一次物品之後,會有一個通知的過程,為了防止多個線程同時通知,造成紊亂,就對通知過程也加鎖了,而這個鎖就是消費用的鎖。當通知過去的時候,消費者會消費完當前物品,直到釋放鎖(消費者消費的過程中一直加鎖解鎖,可以在解鎖的時候,sleep一下,同時也在生產者的過程中sleep一下,可以防止過度的消費或者過度的生產,盡量讓消費者和生產者步調一致,不過sleep的時間不容易把握)。
這是生產線程,在producelock裡面,我們還需要判斷生產是否結束,是為了防止這樣的情況發生,當多個生產者線程被阻塞了,有一個線程完成了所有的生產任務,如果不判斷,其他的生產線程繼續往下執行,直到這個此次循環結束為止,會超出生產的量。
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 對應上面的“通知條件”“等待條件”
https://github.com/CharellKing/Unix-Network-Programming