從epoll構建muduo-13 Reactor + ThreadPool 成型

來源:互聯網
上載者:User

標籤:linux   muduo   socket   多線程   epoll   

mini-muduo版本傳送門
version 0.00 從epoll構建muduo-1 mini-muduo介紹
version 0.01 從epoll構建muduo-2 最簡單的epoll
version 0.02 從epoll構建muduo-3 加入第一個類,順便介紹Reactor
version 0.03 從epoll構建muduo-4 加入Channel
version 0.04 從epoll構建muduo-5 加入Acceptor和TcpConnection
version 0.05 從epoll構建muduo-6 加入EventLoop和Epoll
version 0.06 從epoll構建muduo-7 加入IMuduoUser
version 0.07 從epoll構建muduo-8 加入發送緩衝區和接收緩衝區
version 0.08 從epoll構建muduo-9 加入onWriteComplate回調和Buffer
version 0.09 從epoll構建muduo-10 Timer定時器
version 0.11 從epoll構建muduo-11 單線程Reactor網路模型成型
version 0.12 從epoll構建muduo-12 多線程代碼入場
version 0.13 從epoll構建muduo-13 Reactor + ThreadPool 成型


mini-muduo v0.13版本,mini-muduo完整可運行樣本可從github下載,使用命令git checkout v0.13可切換到此版本,線上瀏覽此版本到這裡

本版是個裡程碑版本,可以通過本版瞭解多線程是如何通過IO線程讀/寫網路資料的,在前一個版本v0.12重點介紹了基礎知識的前提下,本篇著重分析多線程邏輯裡最重要的三個方法EventLoop::runInLoop/EventLoop::queueInLoop/EventLoop::doPendingFunctors。下面逐步介紹本版本修改的細節,三個方法放在最後的EventLoop節。


1 Task類

    這個類是v0.13版本新加入的,它就是一個攜帶參數的回調。它的作用就是閉包(closure),它是我們用來代替muduo裡boost::function和boost:bind的。為什麼不使用boost::function和boost:bind?之前解釋過了,為了不引入新概念,降低mini-muduo的學習成本。這個Task類不太具有通用性(不像BlockingQueue,範型實現),只是為了在本項目裡使用的,Task只支援兩種類型的回調,第一種是無參數的回調,被調用者只需要實現一個”void run0()“,第二種是有兩個參數的回調,被調用者實現"void run2(const string&, void*)"。因為有了Task類,所有需要非同步回調的地方都用它來實現了。

2 TcpConnection

    添加了一個sendInLoop方法,把原來send方法裡的實現移動到了sendInLoop方法裡,而send方法本身變成了一個外部介面的封裝。根據調用send方法所線上程的不同,採取不同的策略,如果調用TcpConnection::send的線程剛好是IO線程,則立刻使用write將資料送出(當然是緩衝區為空白的時候)。如果調用TcpConnection::send的線程是work線程(也就是幕後處理線程)則只將要發送的資訊通過Task對象丟到EventLoop的非同步隊列中,然後立刻返回。EventLoop隨後會在IO線程回調到TcpConnetion::sendInLoop方法,這樣做的目的是保證網路IO相關操作只在IO線程進行。

3 TimerQueue

    改動不大,只是用Task封裝了非同步請求,這樣保證所有關於Timer的操作都在IO線程進行。因為我們就是用timerfd來實現的定時器,而timerfd又是由epoll來監控的,所以這很好理解,對epoll監控的所有檔案描述的操作都要放到IO線程。    

4 EchoServer

    在接到任務後不是立刻處理,而是將任務通過ThreadPool::addTask丟進線程池,使用多執行緒,在真正的處理回調裡,簡單的類比了一個消耗CPU的函數(計算斐波那契數列),通過log可以看到,每次的任務都被分配給了池子裡的不同線程。

5 EventLoop

    1 wakeup方法的實現在上一版本v0.12已經加入,但是調用被注釋掉了。這次調用點位於EventLoop::queueInLoop。這個方法是用喚醒IO線程的,確切的說是喚醒IO線程裡的epoll_wait,只有一點要注意,別忘記在EventLoop::handleRead裡讀出這個uint_64,否則會導致eventfd被持續激發使程式進入無限迴圈。

    2 EventLoop::queueInLoop方法,這個方法在v0.12版本叫queueLoop,為了和原始muduo保持一致,本版改名了。這個方法的作用是將一個非同步回調加入到待執行隊列_pendingFunctors中,與v0.12版本相比第一個差異是本版本對_pendingFunctors加了鎖,這點很好理解,因為EventLoop::queueLoop經常被外部的其他非IO線程調用。第二個修改是添加了一定條件下的wakeup()喚醒。為什麼單線程版本沒有這個喚醒邏輯?因為單線程版本裡所有的非同步呼叫都是在Loop迴圈開始後,doPendingFunctors()之前觸發的,只需要把回調插入_pendingFunctors這個數組即可。但是在多線程版本queueInLoop的入口就很多了,比如下面這3種情況下,都可能調用EventLoop::queueInLoop

        情況 1 IO線程,在IMuduoUser::onMessage回調裡,比如EchoServer::onMessage裡。

        情況 2 IO線程,在doPendingFunctors()執行Task->doTask的實現體裡,比如EchoServer::onWriteComplate裡。

        情況 3 非IO線程,線程池的另一個線程中。

在單線程版本裡,可以不考慮情況3,情況2雖然有可能發生,但是我們當時簡單假設使用者只會在onMessage添加Task,而不會在Task的回調裡再添加Task。所以上一個版本在此處進行了簡單化處理,並不需要wakeup()操作。本版本由於要考慮這幾種情況,所以添加了一些條件判斷和wakeup()調用。

特別要注意_callingPendingFunctors變數。這個變數有點隱晦,我開始在寫程式的時候忽略了它,後來發現它非常重要,在上面的情況2時,如果沒有這個變數,會導致非同步呼叫永遠不會觸發!

    3 EventLoop::runInLoop方法,本版本新添加的方法,與queueInLoop方法非常相似,"runIn"和"queueIn"從名字的差異就可以理解,當外部調用runInLoop的時候,會判斷當前是否為IO線程,如果是在IO線程,則立刻執行Task裡的回調。否則通過調用queueInLoop將Task加入到非同步隊列,等待後續被調用。

    4 EventLoop::doPendingFunctors,這個方法與queueInLoop方法一樣,也是兩處修改,首先是由於多線程操作vector必須要加鎖,另外是添加了_callingPendingFunctors變數的控制,再次強調這個變數非常重要。


本篇的最後,為了更清晰的解釋EventLoop在多線程環境下的邏輯,我畫了一張時序圖,時序圖表達的就是“在非IO線程裡調用TcpConnection::send發送資料”這一動作引發的連鎖調用。這一動作需要3個Loop來完成,涉及4個子調用過程。綠色表明代碼工作在IO線程紅色表示代碼工作在Work線程(背景工作執行緒,真正處理計算任務的線程),在原書中多線程EventLoop的講解位於294頁附近,但是很遺憾,作者沒有為這一過程製作時序圖。我這裡算是補畫一張吧。



相關文章

聯繫我們

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