C/S框架 st_asio_wrapper 開發教程(2019.10.17更新)(四)

如果你偶然瀏覽到這裏,請先看 C/S框架 st_asio_wrapper 開發教程(一)
源代碼及例程下載地址:
git:https://github.com/youngwolf-project/st_asio_wrapper/
QQ交流羣:198941541

九:陷阱

        大家都知道多線程死鎖,進程間死鎖,今天我要說的是,兩臺網絡通信中的電腦,也會死鎖,不可思議吧?那麼st_asip_wrapper會死鎖嗎?請往下看。
        先舉個網絡編程中死鎖的典型例子,假如AB兩個程序做網絡通信(誰是服務端誰是客戶端無所謂),都採用單線程阻塞模式,假設在某個時刻,A因發送緩存滿而被阻塞在send,此時B(AB在不同電腦上,所以理論上,他們完全不相干涉,換句話說,無論A處於什麼業務狀態, B都有可能處於任意一個業務狀態)完全有可能也因爲發送緩存滿而被阻塞在send,由於A被阻塞在send,說明B的接收緩存滿了(不滿的話,數據總是會從A到達B的,數據的接收是由網絡驅動自動做的,不管我們是否接收數據,它總是會在可能的情況下儘量接收數據到本地緩存),由於B被阻塞在send,說明A的接收緩存也滿了(原理同前面所說)。兩個套接字的四個緩存都滿!此時,死鎖就發生了,要想讓A從send返回,B必須從自己的接收緩存裏面讀取一些數據,以便讓A發送一些數據到B。可是不幸的是,B被阻塞在send,它沒機會執行recv來讀取一些數據。反之已然。
        那麼大家爲什麼很少遇到上面的情況呢,因爲四個緩存都滿,需要一些特定的條件,就是兩邊的發送一定要非常快,不一定要一直快,當死鎖一但發生,馬上把速度降下來也沒用了(其實根本就沒速度了,因爲卡住了),死鎖無法自解。如果你對你的產品做壓力測試,相信可能會遇得上的。
        那麼st_asio_wrapper是否會死鎖呢?如果你因爲等待發送緩存可用而阻塞在on_msg_handle,死鎖是有可能發生的,沒錯,多線程異步IO居然要死鎖,其原因是消息處理(on_msg_handle)被阻塞,一但數據接收被停止,那麼情況和單線程阻塞模式是一樣的。
        注意,要不是因爲等待發送緩存可用而阻塞在on_msg_handle(兩端都這樣),都是沒有問題的,大家也不要太害怕,比如你阻塞在on_msg_handle裏面處理自己的業務,或者阻塞在其它地方,比如你自己的線程都是沒有問題的。換句話說,只要不阻塞在service線程裏面,或者即便阻塞在service線程,但解除阻塞的條件與本socket的發送緩存可用性無關,就不會造成死鎖,只會有限的阻塞,死鎖是無限的阻塞。
        更方便簡潔但有些暴力的方法是,以can_overflow爲true調用send_msg(相當於發送緩存永遠可用),這個就需要開發者保證緩存不會達到不可控的直線上升狀態,也就是總要有讓緩存減少的機制,或者業務本身在達到某個高度之後,就會下降,這些情況下,都可以讓can_overflow爲true。
        那麼到底要怎樣做擁塞控制呢,具體請參看教程第五篇

十:線程安全性

        默認情況下service_pump開啓8個IO線程,只要線程數量多於1個,那麼所有的回調方法(以on開關的虛函數),都是併發的(除非是明顯有順序關係的,且在同一個socket對象上的時候),具體說來(以on_msg_handle和on_msg_send爲例):對於同一個socket,on_msg_handle和on_msg_send是併發的,on_msg_handle和on_msg_handle(下一個)、on_msg_send和on_msg_send(下一個)是順序的;對於不同的socket,所有回調都是併發的。
        關於定時器回調的併發性,不同timer之間當然是併發,同一個timer對象的同一個timer(以id區分timer)回調是順序,不同的timer的回調是併發的。注意:多線程對同一個timer對象調用set_timer設置同一個timer(id相同)是不安全的,設計即是這樣(否則每一個timer對象裏面的每一個timer都需要一個互斥對象)。
        其它的方法,除了明顯不應該設計爲多線程的(比如初始化,開始結束服務等),都是線程安全的,比如send_msg等。

十一:關於文件傳輸工具的使用

        這是我寫的兩個基於st_asio_wrapper的demo,file_server和file_client,支持簡單的聊天和文件分塊傳輸,在局域網裏面,最好別分塊,因爲瓶頸在磁盤IO,如果從互聯網上傳輸,則分塊肯定可以提高速度,就像迅雷一樣。
        使用方法很簡單,把要傳輸的文件放到file_server能訪問到的地方,開啓file_server。file_client的運行方式是:file_client link_num,其中每一條連接代表一個文件塊,如果要分5塊同時下載,則link_num設爲5,當file_client所有連接都成功連到服務器時,輸入命令:get file_name1 file_name2 ...,開始以次傳輸每一個文件。輸入其它任何內容則當成聊天信息直接發送。如果傳送的文件不在當前路徑,比如 get ../123/a.txt,則要求客戶端也必須存在../123這個目錄,這個如果你覺得是問題的話,可以修改demo。

st_asio_wrapper使用FAQ
C/S框架 st_asio_wrapper 開發教程(五)
 

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