中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

高性能服務器開發基礎系列 (一)主線程與工作線程的分工

發布時間:2020-07-21 09:20:12 來源:網絡 閱讀:882 作者:張小方32 欄目:建站服務器

服務器端為了能流暢處理多個客戶端鏈接,一般在某個線程A里面accept新的客戶端連接并生成新連接的socket fd,然后將這些新連接的socketfd給另外開的數個工作線程B1、B2、B3、B4,這些工作線程處理這些新連接上的網絡IO事件(即收發數據),同時,還處理系統中的另外一些事務。這里我們將線程A稱為主線程,B1、B2、B3、B4等稱為工作線程。工作線程的代碼框架一般如下:

while (!m_bQuit)  {
    epoll_or_select_func();

    handle_io_events();

    handle_other_things();
}

epoll_or_select_func()中通過select()或者poll/epoll()去檢測socket fd上的io事件,若存在這些事件則下一步handle_io_events()來處理這些事件(收發數據),做完之后可能還要做一些系統其他的任務,即調用handle_other_things()。

這樣做有三個好處:

線程A只需要處理新連接的到來即可,不用處理網絡IO事件。由于網絡IO事件處理一般相對比較慢,如果在線程A里面既處理新連接又處理網絡IO,則可能由于線程忙于處理IO事件,而無法及時處理客戶端的新連接,這是很不好的。

線程A接收的新連接,可以根據一定的負載均衡原則將新的socket fd分配給工作線程。常用的算法,比如round robin,即輪詢機制,即,假設不考慮中途有連接斷開的情況,一個新連接來了分配給B1,又來一個分配給B2,再來一個分配給B3,再來一個分配給B4。如此反復,也就是說線程A記錄了各個工作線程上的socket fd數量,這樣可以最大化地來平衡資源,避免一些工作線程“忙死”,另外一些工作線程“閑死”的現象。

即使工作線程不滿載的情況下,也可以讓工作線程做其他的事情。比如現在有四個工作線程,但只有三個連接。那么線程B4就可以在handle_other_thing()做一些其他事情。

下面討論一個很重要的效率問題:

在上述while循環里面,epoll_or_selec_func()中的epoll_wait/poll/select等函數一般設置了一個超時時間。如果設置超時時間為0,那么在沒有任何網絡IO時間和其他任務處理的情況下,這些工作線程實際上會空轉,白白地浪費cpu時間片。如果設置的超時時間大于0,在沒有網絡IO時間的情況,epoll_wait/poll/select仍然要掛起指定時間才能返回,導致handle_other_thing()不能及時執行,影響其他任務不能及時處理,也就是說其他任務一旦產生,其處理起來具有一定的延時性。這樣也不好。

那如何解決該問題呢?

其實我們想達到的效果是,如果沒有網絡IO時間和其他任務要處理,那么這些工作線程最好直接掛起而不是空轉;如果有其他任務要處理,這些工作線程要立刻能處理這些任務而不是在epoll_wait/poll/selec掛起指定時間后才開始處理這些任務。

我們采取如下方法來解決該問題,以linux為例,不管epoll_fd上有沒有文件描述符fd,我們都給它綁定一個默認的fd,這個fd被稱為喚醒fd。當我們需要處理其他任務的時候,向這個喚醒fd上隨便寫入1個字節的,這樣這個fd立即就變成可讀的了,epoll_wait()/poll()/select()函數立即被喚醒,并返回,接下來馬上就能執行handle_other_thing(),其他任務得到處理。反之,沒有其他任務也沒有網絡IO事件時,epoll_or_select_func()就掛在那里什么也不做。

這個喚醒fd,在linux平臺上可以通過以下幾種方法實現:

管道pipe,創建一個管道,將管道綁定到epoll_fd上。需要時,向管道一端寫入一個字節,工作線程立即被喚醒。

linux 2.6新增的eventfd:

int eventfd(unsigned int initval, int flags); 

步驟也是一樣,將生成的eventfd綁定到epoll_fd上。需要時,向這個 eventfd上寫入一個字節,工作線程立即被喚醒。

第三種方法最方便。即linux特有的socketpair,socketpair是一對相互連接的socket,相當于服務器端和客戶端的兩個端點,每一端都可以讀寫數據。

int socketpair(int domain, int type, int protocol, int sv[2]);

調用這個函數返回的兩個socket句柄就是sv[0],和sv[1],在一個其中任何一個寫入字節,在另外一個收取字節。

將收取的字節的socket綁定到epoll_fd上。需要時,向另外一個寫入的socket上寫入一個字節,工作線程立即被喚醒。

如果是使用socketpair,那么domain參數一定要設置成AFX_UNIX。

由于在windows,select函數只支持檢測socket這一種fd,所以windows上一般只能用方法3的原理。而且需要手動創建兩個socket,然后一個連接另外一個,將讀取的那一段綁定到select的fd上去。這在寫跨兩個平臺代碼時,需要注意的地方。

歡迎關注公眾號『easyserverdev』。如果有任何技術或者職業方面的問題需要我提供幫助,可通過這個公眾號與我取得聯系,此公眾號不僅分享高性能服務器開發經驗和故事,同時也免費為廣大技術朋友提供技術答疑和職業解惑,您有任何問題都可以在微信公眾號直接留言,我會盡快回復您。
高性能服務器開發基礎系列 (一)主線程與工作線程的分工

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

张家界市| 渑池县| 太仓市| 尤溪县| 五原县| 青冈县| 洞口县| 卢湾区| 会泽县| 青州市| 玛多县| 乐亭县| 仙桃市| 唐山市| 得荣县| 洞头县| 西华县| 邯郸市| 阿荣旗| 安陆市| 云阳县| 瓮安县| 青川县| 沧州市| 大同市| 诏安县| 巴林左旗| 上高县| 柘城县| 都匀市| 珠海市| 邛崃市| 永清县| 云阳县| 淮北市| 车致| 沁水县| 大新县| 大城县| 忻城县| 阿克苏市|