生產者-消費者?

問題描述

在寫調度模塊遇到一個問題。有多個線程負責和各個工作節點通信,在工作節點持續工作的過程中,每完成一個小任務就會返回相應的結果。這時需要將各個線程的結果彙總(這裏簡單理解爲對錶示任務進度的變量進行修改),怎麼實現呢?

想法1

一個很直觀的想法是,各線程都向這個共享變量寫入結果。顯然,這裏面臨的就是互斥問題。多個線程同時寫,必然會出問題,在此就不說原因了。解決的方法也很簡單,加鎖。帶來的問題也很明顯,效率低,當這時候有100個線程要寫這個變量時,需要排成隊一個一個寫。(記得Linux內核中說到過這個問題。等下一定要看看)

想法2

各個線程得到結果後,不去立即修改這個值,而是把自己想要改變的量先保存下來。保存之後通專門負責更新的線程,自己有新的進度需要加到總進度上。更新線程中收到通知後,自己去取這個量。這樣一來各個工作線程就不需要阻塞在那裏去獲取共享變量了。而更新線程再收到通知後,自己再決定什麼時候去處理。方法上也更靈活。

目前只想到這個解決方法,有新的想法再更新

想法3

一覺醒來,突然想到第二個想法也會有問題。雖然把工作線程解放了,但更新線程會變得很忙。100個線程不停地notify,更新線程怎麼處理的過來。如果有的通知信息沒有處理到,便會遺漏信息。要怎麼辦?工作線程更新數據後設置一個標識,讓更新線程週期性的去判斷各個標識決定是否去取數據似乎是一個可行的方法。但拿數據的週期爲多少是一個問題,實時性無法得到保證。而且這種方法很可能也會出現不一致的問題。例如,更新線程正在讀取某工作線程更新的最新數據時,該工作線程此時卻又append新的數據。此種情況,要麼是正在讀取的數據會錯誤地變成“新數據”,或者新append的數據變成舊數據,這取決於何時去更新標誌。這似乎也避免不了要加鎖。雖然此種情況最多隻有一個讀和一個寫。但這個加鎖和之前有所不同,對工作線程的影響沒那麼大,因爲只有更新線程剛好和工作線程衝突時纔會有影響。

總結

面臨的問題實際上可描述爲多個生產者,一個消費者的模型。而生產者消費者模型的核心問題應該是對緩衝區的使用上。如果生產者和消費者都只有一個,那無疑只使用一個緩衝區即可,生產者放產品的時候只需要關心緩衝區是否已滿(這裏我們可以看作沒有上限?),而消費者在取產品的時候只需要判斷是否爲空。一個生產者多個消費者,同樣只需要一個緩衝區,除非消費者需要“定製產品”,實際上,一個生產者多個消費者的場景更多見,如線程池、socket中的accept等,而一個著名的問題就是“驚羣效應”,正因爲經典,現在已經有成熟的方法來解決這類問題,核心思想應該是對每個消費者“區別對待”,指名道姓由誰來消費剛生產出來的這個產品。多個生產者一個消費者的情況就靈活多了,既可以所有的生產者把產品放入一個共享的緩衝區中,也可以讓每個生產者擁有自己的緩衝區,再或者把生產者分組,一組生產者共用一個緩衝區,這樣就有n個緩衝區(1<=n<=生產者數量)。至於哪一種方法更好,則取決於具體情況。一個直觀的感覺是,當生產頻率越低,則越應該採用共用緩衝區的方式。一個極端的例子是,所有的生產者的生產頻率都極低,這時候如果還是每個生產者各自有一個緩衝區,那麼消費者很多的工作就會浪費在判斷各個緩衝區是否爲空上。相反,另一個極端的例子是,如果生產者的生產頻率極高,若共用同一個緩衝區,那麼在互斥訪問緩衝區時,生產者大部分時間會浪費在排隊上面。(所以,有方法可以實現不需要互斥訪問嗎?)

C++之父說過:別去猜想性能如何,證明性能的好壞,需要做的就是測試。原話並非如此,大概是這意思吧。

突然感覺這個問題水很深。瞎想了一通,思路有點混亂,以後要多總結!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章