導航:首頁 > 文檔加密 > linux多線程服務端編程pdf

linux多線程服務端編程pdf

發布時間:2022-03-06 20:22:33

A. 如何看懂《linux多線程服務端編程

一:進程和線程
每個進程有自己獨立的地址空間。「在同一個進程」還是「不在同一個進程」是系統功能劃分的重要決策點。《Erlang程序設計》[ERL]把進程比喻為人:
每個人有自己的記憶(內存),人與人通過談話(消息傳遞)來交流,談話既可以是面談(同一台伺服器),也可以在電話里談(不同的伺服器,有網路通信)。面談和電話談的區別在於,面談可以立即知道對方是否死了(crash,SIGCHLD),而電話談只能通過周期性的心跳來判斷對方是否還活著。
有了這些比喻,設計分布式系統時可以採取「角色扮演」,團隊里的幾個人各自扮演一個進程,人的角色由進程的代碼決定(管登錄的、管消息分發的、管買賣的等等)。每個人有自己的記憶,但不知道別人的記憶,要想知道別人的看法,只能通過交談(暫不考慮共享內存這種IPC)。然後就可以思考:
·容錯:萬一有人突然死了
·擴容:新人中途加進來
·負載均衡:把甲的活兒挪給乙做
·退休:甲要修復bug,先別派新任務,等他做完手上的事情就把他重啟
等等各種場景,十分便利。

線程的特點是共享地址空間,從而可以高效地共享數據。一台機器上的多個進程能高效地共享代碼段(操作系統可以映射為同樣的物理內存),但不能共享數據。如果多個進程大量共享內存,等於是把多進程程序當成多線程來寫,掩耳盜鈴。
「多線程」的價值,我認為是為了更好地發揮多核處理器(multi-cores)的效能。在單核時代,多線程沒有多大價值(個人想法:如果要完成的任務是CPU密集型的,那多線程沒有優勢,甚至因為線程切換的開銷,多線程反而更慢;如果要完成的任務既有CPU計算,又有磁碟或網路IO,則使用多線程的好處是,當某個線程因為IO而阻塞時,OS可以調度其他線程執行,雖然效率確實要比任務的順序執行效率要高,然而,這種類型的任務,可以通過單線程的」non-blocking IO+IO multiplexing」的模型(事件驅動)來提高效率,採用多線程的方式,帶來的可能僅僅是編程上的簡單而已)。Alan Cox說過:」A computer is a state machine.Threads are for people who can』t program state machines.」(計算機是一台狀態機。線程是給那些不能編寫狀態機程序的人准備的)如果只有一塊CPU、一個執行單元,那麼確實如Alan Cox所說,按狀態機的思路去寫程序是最高效的。

二:單線程伺服器的常用編程模型
據我了解,在高性能的網路程序中,使用得最為廣泛的恐怕要數」non-blocking IO + IO multiplexing」這種模型,即Reactor模式。
在」non-blocking IO + IO multiplexing」這種模型中,程序的基本結構是一個事件循環(event loop),以事件驅動(event-driven)和事件回調的方式實現業務邏輯:
[cpp] view plain
//代碼僅為示意,沒有完整考慮各種情況
while(!done)
{
int timeout_ms = max(1000, getNextTimedCallback());
int retval = poll(fds, nfds, timeout_ms);
if (retval<0){
處理錯誤,回調用戶的error handler
}else{
處理到期的timers,回調用戶的timer handler
if(retval>0){
處理IO事件,回調用戶的IO event handler
}
}
}

這里select(2)/poll(2)有伸縮性方面的不足(描述符過多時,效率較低),Linux下可替換為epoll(4),其他操作系統也有對應的高性能替代品。
Reactor模型的優點很明顯,編程不難,效率也不錯。不僅可以用於讀寫socket,連接的建立(connect(2)/accept(2)),甚至DNS解析都可以用非阻塞方式進行,以提高並發度和吞吐量(throughput),對於IO密集的應用是個不錯的選擇。lighttpd就是這樣,它內部的fdevent結構十分精妙,值得學習。
基於事件驅動的編程模型也有其本質的缺點,它要求事件回調函數必須是非阻塞的。對於涉及網路IO的請求響應式協議,它容易割裂業務邏輯,使其散布於多個回調函數之中,相對不容易理解和維護。

三:多線程伺服器的常用編程模型
大概有這么幾種:
a:每個請求創建一個線程,使用阻塞式IO操作。在java 1.4引人NIO之前,這是Java網路編程的推薦做法。可惜伸縮性不佳(請求太多時,操作系統創建不了這許多線程)。
b:使用線程池,同樣使用阻塞式IO操作。與第1種相比,這是提高性能的措施。
c:使用non-blocking IO + IO multiplexing。即Java NIO的方式。
d:Leader/Follower等高級模式。
在默認情況下,我會使用第3種,即non-blocking IO + one loop per thread模式來編寫多線程C++網路服務程序。

1:one loop per thread
此種模型下,程序里的每個IO線程有一個event loop,用於處理讀寫和定時事件(無論周期性的還是單次的)。代碼框架跟「單線程伺服器的常用編程模型」一節中的一樣。
libev的作者說:
One loop per thread is usually a good model. Doing this is almost never wrong, some times a better-performance model exists, but it is always a good start.

這種方式的好處是:
a:線程數目基本固定,可以在程序啟動的時候設置,不會頻繁創建與銷毀。
b:可以很方便地在線程間調配負載。
c:IO事件發生的線程是固定的,同一個TCP連接不必考慮事件並發。

Event loop代表了線程的主循環,需要讓哪個線程幹活,就把timer或IO channel(如TCP連接)注冊到哪個線程的loop里即可:對實時性有要求的connection可以單獨用一個線程;數據量大的connection可以獨佔一個線程,並把數據處理任務分攤到另幾個計算線程中(用線程池);其他次要的輔助性connections可以共享一個線程。
比如,在dbproxy中,一個線程用於專門處理客戶端發來的管理命令;一個線程用於處理客戶端發來的MySQL命令,而與後端資料庫通信執行該命令時,是將該任務分配給所有事件線程處理的。

對於non-trivial(有一定規模)的服務端程序,一般會採用non-blocking IO + IO multiplexing,每個connection/acceptor都會注冊到某個event loop上,程序里有多個event loop,每個線程至多有一個event loop。
多線程程序對event loop提出了更高的要求,那就是「線程安全」。要允許一個線程往別的線程的loop里塞東西,這個loop必須得是線程安全的。
在dbproxy中,線程向其他線程分發任務,是通過管道和隊列實現的。比如主線程accept到連接後,將表示該連接的結構放入隊列,並向管道中寫入一個位元組。計算線程在自己的event loop中注冊管道的讀事件,一旦有數據可讀,就嘗試從隊列中取任務。

2:線程池
不過,對於沒有IO而光有計算任務的線程,使用event loop有點浪費。可以使用一種補充方案,即用blocking queue實現的任務隊列:
[cpp] view plain
typedef boost::function<void()>Functor;
BlockingQueue<Functor> taskQueue; //線程安全的全局阻塞隊列

//計算線程
void workerThread()
{
while (running) //running變數是個全局標志
{
Functor task = taskQueue.take(); //this blocks
task(); //在產品代碼中需要考慮異常處理
}
}

// 創建容量(並發數)為N的線程池
int N = num_of_computing_threads;
for (int i = 0; i < N; ++i)
{
create_thread(&workerThread); //啟動線程
}

//向任務隊列中追加任務
Foo foo; //Foo有calc()成員函數
boost::function<void()> task = boost::bind(&Foo::calc,&foo);
taskQueue.post(task);

除了任務隊列,還可以用BlockingQueue<T>實現數據的生產者消費者隊列,即T是數據類型而非函數對象,queue的消費者從中拿到數據進行處理。其實本質上是一樣的。

3:總結
總結而言,我推薦的C++多線程服務端編程模式為:one (event) loop per thread + thread pool:
event loop用作IO multiplexing,配合non-blockingIO和定時器;
thread pool用來做計算,具體可以是任務隊列或生產者消費者隊列。

以這種方式寫伺服器程序,需要一個優質的基於Reactor模式的網路庫來支撐,muo正是這樣的網路庫。比如dbproxy使用的是libevent。
程序里具體用幾個loop、線程池的大小等參數需要根據應用來設定,基本的原則是「阻抗匹配」(解釋見下),使得CPU和IO都能高效地運作。所謂阻抗匹配原則:
如果池中線程在執行任務時,密集計算所佔的時間比重為 P (0 < P <= 1),而系統一共有 C 個 CPU,為了讓這 C 個 CPU 跑滿而又不過載,線程池大小的經驗公式 T = C/P。(T 是個 hint,考慮到 P 值的估計不是很准確,T 的最佳值可以上下浮動 50%)
以後我再講這個經驗公式是怎麼來的,先驗證邊界條件的正確性。
假設 C = 8,P = 1.0,線程池的任務完全是密集計算,那麼T = 8。只要 8 個活動線程就能讓 8 個 CPU 飽和,再多也沒用,因為 CPU 資源已經耗光了。
假設 C = 8,P = 0.5,線程池的任務有一半是計算,有一半等在 IO 上,那麼T = 16。考慮操作系統能靈活合理地調度 sleeping/writing/running 線程,那麼大概 16 個「50%繁忙的線程」能讓 8 個 CPU 忙個不停。啟動更多的線程並不能提高吞吐量,反而因為增加上下文切換的開銷而降低性能。
如果 P < 0.2,這個公式就不適用了,T 可以取一個固定值,比如 5*C。

另外,公式里的 C 不一定是 CPU 總數,可以是「分配給這項任務的 CPU 數目」,比如在 8 核機器上分出 4 個核來做一項任務,那麼 C=4。

四:進程間通信只用TCP
Linux下進程間通信的方式有:匿名管道(pipe)、具名管道(FIFO)、POSIX消息隊列、共享內存、信號(signals),以及Socket。同步原語有互斥器(mutex)、條件變數(condition variable)、讀寫鎖(reader-writer lock)、文件鎖(record locking)、信號量(semaphore)等等。

進程間通信我首選Sockets(主要指TCP,我沒有用過UDP,也不考慮Unix domain協議)。其好處在於:
可以跨主機,具有伸縮性。反正都是多進程了,如果一台機器的處理能力不夠,很自然地就能用多台機器來處理。把進程分散到同一區域網的多台機器上,程序改改host:port配置就能繼續用;
TCP sockets和pipe都是操作文件描述符,用來收發位元組流,都可以read/write/fcntl/select/poll等。不同的是,TCP是雙向的,Linux的pipe是單向的,進程間雙向通信還得開兩個文件描述符,不方便;而且進程要有父子關系才能用pipe,這些都限制了pipe的使用;
TCP port由一個進程獨占,且進程退出時操作系統會自動回收文件描述符。因此即使程序意外退出,也不會給系統留下垃圾,程序重啟之後能比較容易地恢復,而不需要重啟操作系統(用跨進程的mutex就有這個風險);而且,port是獨占的,可以防止程序重復啟動,後面那個進程搶不到port,自然就沒法初始化了,避免造成意料之外的結果;
與其他IPC相比,TCP協議的一個天生的好處是「可記錄、可重現」。tcpmp和Wireshark是解決兩個進程間協議和狀態爭端的好幫手,也是性能(吞吐量、延遲)分析的利器。我們可以藉此編寫分布式程序的自動化回歸測試。也可以用tcp之類的工具進行壓力測試。TCP還能跨語言,服務端和客戶端不必使用同一種語言。

分布式系統的軟體設計和功能劃分一般應該以「進程」為單位。從宏觀上看,一個分布式系統是由運行在多台機器上的多個進程組成的,進程之間採用TCP長連接通信。
使用TCP長連接的好處有兩點:一是容易定位分布式系統中的服務之間的依賴關系。只要在機器上運行netstat -tpna|grep <port>就能立刻列出用到某服務的客戶端地址(Foreign Address列),然後在客戶端的機器上用netstat或lsof命令找出是哪個進程發起的連接。TCP短連接和UDP則不具備這一特性。二是通過接收和發送隊列的長度也較容易定位網路或程序故障。在正常運行的時候,netstat列印的Recv-Q和Send-Q都應該接近0,或者在0附近擺動。如果Recv-Q保持不變或持續增加,則通常意味著服務進程的處理速度變慢,可能發生了死鎖或阻塞。如果Send-Q保持不變或持續增加,有可能是對方伺服器太忙、來不及處理,也有可能是網路中間某個路由器或交換機故障造成丟包,甚至對方伺服器掉線,這些因素都可能表現為數據發送不出去。通過持續監控Recv-Q和Send-Q就能及早預警性能或可用性故障。以下是服務端線程阻塞造成Recv-Q和客戶端Send-Q激增的例子:
[cpp] view plain
$netstat -tn
Proto Recv-Q Send-Q Local Address Foreign
tcp 78393 0 10.0.0.10:2000 10.0.0.10:39748 #服務端連接
tcp 0 132608 10.0.0.10:39748 10.0.0.10:2000 #客戶端連接
tcp 0 52 10.0.0.10:22 10.0.0.4:55572

五:多線程伺服器的適用場合
如果要在一台多核機器上提供一種服務或執行一個任務,可用的模式有:
a:運行一個單線程的進程;
b:運行一個多線程的進程;
c:運行多個單線程的進程;
d:運行多個多線程的進程;

考慮這樣的場景:如果使用速率為50MB/s的數據壓縮庫,進程創建銷毀的開銷是800微秒,線程創建銷毀的開銷是50微秒。如何執行壓縮任務?
如果要偶爾壓縮1GB的文本文件,預計運行時間是20s,那麼起一個進程去做是合理的,因為進程啟動和銷毀的開銷遠遠小於實際任務的耗時。
如果要經常壓縮500kB的文本數據,預計運行時間是10ms,那麼每次都起進程 似乎有點浪費了,可以每次單獨起一個線程去做。
如果要頻繁壓縮10kB的文本數據,預計運行時間是200微秒,那麼每次起線程似 乎也很浪費,不如直接在當前線程搞定。也可以用一個線程池,每次把壓縮任務交給線程池,避免阻塞當前線程(特別要避免阻塞IO線程)。
由此可見,多線程並不是萬靈丹(silver bullet)。

1:必須使用單線程的場合
據我所知,有兩種場合必須使用單線程:
a:程序可能會fork(2);
實際編程中,應該保證只有單線程程序能進行fork(2)。多線程程序不是不能調用fork(2),而是這么做會遇到很多麻煩:
fork一般不能在多線程程序中調用,因為Linux的fork只克隆當前線程的thread of control,不可隆其他線程。fork之後,除了當前線程之外,其他線程都消失了。
這就造成一種危險的局面。其他線程可能正好處於臨界區之內,持有了某個鎖,而它突然死亡,再也沒有機會去解鎖了。此時如果子進程試圖再對同一個mutex加鎖,就會立即死鎖。因此,fork之後,子進程就相當於處於signal handler之中(因為不知道調用fork時,父進程中的線程此時正在調用什麼函數,這和信號發生時的場景一樣),你不能調用線程安全的函數(除非它是可重入的),而只能調用非同步信號安全的函數。比如,fork之後,子進程不能調用:
malloc,因為malloc在訪問全局狀態時幾乎肯定會加鎖;
任何可能分配或釋放內存的函數,比如snprintf;
任何Pthreads函數;
printf系列函數,因為其他線程可能恰好持有stdout/stderr的鎖;
除了man 7 signal中明確列出的信號安全函數之外的任何函數。

因此,多線程中調用fork,唯一安全的做法是fork之後,立即調用exec執行另一個程序,徹底隔斷子進程與父進程的聯系。

在多線程環境中調用fork,產生子進程後。子進程內部只存在一個線程,也就是父進程中調用fork的線程的副本。
使用fork創建子進程時,子進程通過繼承整個地址空間的副本,也從父進程那裡繼承了所有互斥量、讀寫鎖和條件變數的狀態。如果父進程中的某個線程佔有鎖,則子進程同樣佔有這些鎖。問題是子進程並不包含佔有鎖的線程的副本,所以子進程沒有辦法知道它佔有了哪些鎖,並且需要釋放哪些鎖。
盡管Pthread提供了pthread_atfork函數試圖繞過這樣的問題,但是這回使得代碼變得混亂。因此《Programming With Posix Threads》一書的作者說:」Avoid using fork in threaded code except where the child process will immediately exec a new program.」。

b:限製程序的CPU佔用率;
這個很容易理解,比如在一個8核的伺服器上,一個單線程程序即便發生busy-wait,占滿1個core,其CPU使用率也只有12.5%,在這種最壞的情況下,系統還是有87.5%的計算資源可供其他服務進程使用。
因此對於一些輔助性的程序,如果它必須和主要服務進程運行在同一台機器的話,那麼做成單線程的能避免過分搶奪系統的計算資源。

B. 《Linux高性能伺服器編程》pdf下載在線閱讀全文,求百度網盤雲資源

《Linux高性能伺服器編程》(游雙)電子書網盤下載免費在線閱讀

鏈接:

提取碼: jxb9

書名:Linux高性能伺服器編程

作者:游雙

豆瓣評分:7.9

出版社:機械工業出版社

出版年份:2013-5-1

頁數:360

內容簡介:

本書是Linux伺服器編程領域的經典著作,由資深Linux軟體開發工程師撰寫,從網路協議、伺服器編程核心要素、原理機制、工具框架等多角度全面闡釋了編寫高性能Linux伺服器應用的方法、技巧和思想。不僅理論全面、深入,抓住了重點和難點,還包含兩個綜合性案例,極具實戰意義。

全書共17章,分為3個部分:第一部分對Linux伺服器編程的核心基礎——TCP/IP協議進行了深入的解讀和闡述,包括TCP/IP協議族、TCP/IP協議,以及一個經典的TCP/IP通信案例;第二部分對高性能伺服器編程的核心要素進行了全面深入的剖析,包含Linux網路編程API、高級I/O函數、Linux伺服器程序規范、高性能伺服器程序框架、I/O復用、信號、定時器、高性能I/O框架庫Libevent、多進程編程、多線程編程、進程池和線程池等內容,原理、技術與方法並重;第三部分從側重實戰的角度講解了高性能伺服器的優化與監測,包含伺服器的調制、調試和測試,以及各種實用系統監測工具的使用等內容。

作者簡介:

游雙,資深Linux軟體開發工程師,對Linux網路編程,尤其是伺服器端的編程,有非常深入的研究,實戰經驗也十分豐富。曾就職於摩托羅拉,擔任高級Linux軟體工程師。此外,他還精通C++、Android、QT等相關的技術。活躍於Chinaunix等專業技術社區,發表了大量關於Linux網路編程的文章,深受社區歡迎。


C. 有學linux的書籍推薦嗎

D. 本人面試的javaweb,這是在做linux運維嗎

可能公司的javaweb項目今後是要放在liunx系統伺服器中的,也就是在此之前你需要學會liunx的基本應用,而且現在liunx系統在伺服器一塊應用很廣泛,你看看你們公司是否有開發javaee的,如崗位果有的話你今後應該會被調到開發javaee中,如果沒有或者人很少且不缺人的話,你可能較長一段時間都要在做liunx維護。

E. linux下伺服器程序的幾種基本模型

我總結下來有這么幾種:

F. linux多線程服務端編程 看什麼書

這本書主要分享了作者在實現公司內部的分布式服務系統中積累的多線程和網路編程方面的經驗,並介紹了C++ 在編寫這種分布式系統的服務端程序時的功能取捨與注意事項,書中的很多決策(design decision)是在這一應用場景下做出的。
這本書沒有細談分布式系統的設計,只在第9章列舉了分布式系統的挑戰及其對程序設計(服務端編程)的影響,例如可靠性、可維護性等。

G. 想接觸C++多線程編程,需要從哪方面入手,有沒有

多線程編程的難點不在於鎖,正常人看一下操作系統再寫幾個線程demo就可以基本理解了。對於C++而言,甚至連編寫線程安全的類也不是難事。只需要用同步原語來保持對共享資源的訪問即可。
我個人覺得最需要的就是實戰,寫Demo誰都會寫。同步原語就那麼幾個,信號量,互斥量,條件變數等。但是怎麼用呢?當你從點擊星際爭霸到和玩家匹配進行游戲,這當中程序是怎麼運行的?
事件驅動是怎麼驅動的?
就目前來說,我遇到的困難不是線程的死鎖,而是對並發模型的理解。Actor,Reactor模式等。這些東西不實戰,個人空想理解起來會吃力。
推薦《Linux多線程服務端編程》,這本書給我的觀點是實戰性很強,而且涉及面也比較廣。後幾章提到了分布式系統和作者對C++的思考以及STL algotrithm的運用。如作者所說:「對於面向對象,封裝式必須的;但繼承和多態耦合性太強,很不劃算」我就很贊同

同時展示了一個用C++開發的網路庫,不過雖然看了這本書,我還是沒找到為什麼要用C++的理由。我認為C的確可以很好地解決問題。C++的話就RAII算是真的有益處。
但讀之前你需要有一定的C++和操作系統基礎。當時買這本書的時候還覺得有點心疼,現在看看物超所值。(我那本CSAPP就翻了一章=-=)
總結:看現代操作系統第二章,同時結合C++11的thread庫寫經典Demo(生產者消費者問題等)
花兩周左右。剩下的就是實戰。如果不實戰,你還是不知道這些東西在生產環境中是怎麼使用的。
可以結合muo skynet等開源網路框架學習並發模型。

H. 陳碩 linux 多線程服務端編程 怎樣生成可執行文件

進程和線程 每個進程有自己獨立的地址空間。「在同一個進程」還是「不在同一個進程」是系統功能劃分的重要決策點。《Erlang程序設計》[ERL]把進程比喻為人: 每個人有自己的記憶(內存),人與人通過談話(消息傳遞)來交流

I. linux 伺服器程序接受請求,是單線程好,還是多線程好

看處理邏輯。如果一個處理邏輯申請大量資源,建議多進程。
如果是輕量級別,建議多線程。

J. 如何看懂《Linux多線程服務端編程

:進程線程 每進程自獨立址空間同進程同進程系統功能劃重要決策點《Erlang程序設計》[ERL]進程比喻: 每自記憶(內存)與通談(消息傳遞)交流談既面談(同台伺服器)電談(同伺服器中國絡通信)面談電談區別於面談立即知道否死(crash,SIGCHLD)電談能通周期性跳判斷否著 些比喻設計布式系統採取角色扮演團隊幾各自扮演進程角色由進程代碼決定(管登錄、管消息發、管買賣等等)每自記憶知道別記憶要想知道別看能通交談(暫考慮共享內存種IPC)思考: ·容錯:萬突死 ·擴容:新途加進 ·負載均衡:甲挪給乙做 ·退休:甲要修復bug先別派新任務等做完手事情重啟 等等各種場景十便利 線程特點共享址空間高效共享數據台機器進程能高效共享代碼段(操作系統映射同物理內存)能共享數據進程量共享內存等於進程程序線程寫掩耳盜鈴 線程價值我認更發揮核處理器(multi-cores)效能單核代線程沒價值(想:要完任務CPU密集型線程沒優勢甚至線程切換銷線程反更慢;要完任務既CPU計算磁碟或中國絡IO則使用線程處某線程IO阻塞OS調度其線程執行雖效率確實要比任務順序執行效率要高種類型任務通單線程non-blocking IO+IO multiplexing模型(事件驅)提高效率採用線程式帶能僅僅編程簡單已)Alan Cox說:A 中國puter is a state machine.Threads are for people who can』t program state machines.(計算機台狀態機線程給些能編寫狀態機程序准備)塊CPU、執行單元確實Alan Cox所說按狀態機思路寫程序高效 二:單線程伺服器用編程模型 據我解高性能中國絡程序使用廣泛恐怕要數non-blocking IO + IO multiplexing種模型即Reactor模式 non-blocking IO + IO multiplexing種模型程序基本結構事件循環(event loop)事件驅(event-driven)事件調式實現業務邏輯: [cpp] view plain //代碼僅示意沒完整考慮各種情況 while(!done) { int timeout_ms = max(1000, getNextTimedCallback()); int retval = poll(fds, nfds, timeout_ms); if (retval0){ 處理IO事件調用戶IO event handler } } } select(2)/poll(2)伸縮性面足(描述符效率較低)Linux替換epoll(4)其操作系統應高性能替代品 Reactor模型優點明顯編程難效率錯僅用於讀寫socket連接建立(connect(2)/accept(2))甚至DNS解析都用非阻塞式進行提高並發度吞吐量(throughput)於IO密集應用錯選擇lighttpd內部fdevent結構十精妙值習 基於事件驅編程模型其本質缺點要求事件調函數必須非阻塞於涉及中國絡IO請求響應式協議容易割裂業務邏輯使其散布於調函數相容易理解維護 三:線程伺服器用編程模型 概幾種: a:每請求創建線程使用阻塞式IO操作Java 1.4引NIO前Java中國絡編程推薦做惜伸縮性佳(請求太操作系統創建許線程) b:使用線程池同使用阻塞式IO操作與第1種相比提高性能措施 c:使用non-blocking IO + IO multiplexing即Java NIO式 d:Leader/Follower等高級模式 默認情況我使用第3種即non-blocking IO + one loop per thread模式編寫線程C++中國絡服務程序 1:one loop per thread 種模型程序每IO線程event loop用於處理讀寫定事件(論周期性單)代碼框架跟單線程伺服器用編程模型節 libev作者說: One loop per thread is usually a good model. Doing this is almost never wrong, some times a better-performance model exists, but it is always a good start. 種式處: a:線程數目基本固定程序啟候設置頻繁創建與銷毀 b:便線程間調配負載 c:IO事件發線程固定同TCP連接必考慮事件並發 Event loop代表線程主循環需要讓哪線程干timer或IO channel(TCP連接)注冊哪線程loop即:實性要求connection單獨用線程;數據量connection獨占線程並數據處理任務攤另幾計算線程(用線程池);其要輔助性connections共享線程 比dbproxy線程用於專門處理客戶端發管理命令;線程用於處理客戶端發MySQL命令與端資料庫通信執行該命令該任務配給所事件線程處理 於non-trivial(定規模)服務端程序般採用non-blocking IO + IO multiplexing每connection/acceptor都注冊某event loop程序event loop每線程至event loop 線程程序event loop提更高要求線程安全要允許線程往別線程loop塞東西loop必須線程安全 dbproxy線程向其線程發任務通管道隊列實現比主線程accept連接表示該連接結構放入隊列並向管道寫入位元組計算線程自event loop注冊管道讀事件旦數據讀嘗試隊列取任務 2:線程池 於沒IO光計算任務線程使用event loop點浪費使用種補充案即用blocking queue實現任務隊列: [cpp] view plain typedef boost::functionFunctor; BlockingQueue taskQueue; //線程安全全局阻塞隊列 //計算線程 void workerThread() { while (running) //running變數全局標志 { Functor task = taskQueue.take(); //this blocks task(); //產品代碼需要考慮異處理 } } // 創建容量(並發數)N線程池 int N = num_of_中國puting_threads; for (int i = 0; i < N; ++i) { create_thread(&workerThread); //啟線程 } //向任務隊列追加任務 Foo foo; //Foocalc()員函數 boost::function task = boost::bind(&Foo::calc&foo); taskQueue.post(task); 除任務隊列用BlockingQueue實現數據產者消費者隊列即T數據類型非函數象queue消費者拿數據進行處理其實本質 3:總結 總結言我推薦C++線程服務端編程模式:one (event) loop per thread + thread pool: event loop用作IO multiplexing配合non-blockingIO定器; thread pool用做計算具體任務隊列或產者消費者隊列 種式寫伺服器程序需要優質基於Reactor模式中國絡庫支撐muo中國絡庫比dbproxy使用libevent 程序具體用幾loop、線程池等參數需要根據應用設定基本原則阻抗匹配(解釋見)使CPUIO都能高效運作所謂阻抗匹配原則: 池線程執行任務密集計算所佔間比重 P (0 < P <= 1)系統共 C CPU讓 C CPU 跑滿載線程池經驗公式 T = C/P(T hint考慮 P 值估計准確T 佳值浮 50%) 我再講經驗公式先驗證邊界條件確性 假設 C = 8P = 1.0線程池任務完全密集計算T = 8要 8 線程能讓 8 CPU 飽再沒用 CPU 資源已經耗光 假設 C = 8P = 0.5線程池任務半計算半等 IO T = 16考慮操作系統能靈合理調度 sleeping/writing/running 線程概 16 50%繁忙線程能讓 8 CPU 忙停啟更線程並能提高吞吐量反增加文切換銷降低性能 P < 0.2公式適用T 取固定值比 5*C 另外公式 C 定 CPU 總數配給項任務 CPU 數目比 8 核機器 4 核做項任務 C=4 四:進程間通信用TCP Linux進程間通信式:匿名管道(pipe)、具名管道(FIFO)、POSIX消息隊列、共享內存、信號(signals)及Socket同步原語互斥器(mutex)、條件變數(condition variable)、讀寫鎖(reader-writer lock)、文件鎖(record locking)、信號量(semaphore)等等 進程間通信我首選Sockets(主要指TCP我沒用UDP考慮Unix domain協議)其處於: 跨主機具伸縮性反都進程台機器處理能力夠自能用台機器處理進程散同局域中國台機器程序改改host:port配置能繼續用; TCP socketspipe都操作文件描述符用收發位元組流都read/write/fcntl/select/poll等同TCP雙向Linuxpipe單向進程間雙向通信兩文件描述符便;且進程要父關系才能用pipe些都限制pipe使用; TCP port由進程獨占且進程退操作系統自收文件描述符即使程序意外退給系統留垃圾程序重啟能比較容易恢復需要重啟操作系統(用跨進程mutex風險);且port獨占防止程序重復啟面進程搶port自沒初始化避免造意料外結; 與其IPC相比TCP協議處記錄、重現tcpmpWireshark解決兩進程間協議狀態爭端幫手性能(吞吐量、延遲)析利器我借編寫布式程序自化歸測試用tcp類工具進行壓力測試TCP能跨語言服務端客戶端必使用同種語言 布式系統軟體設計功能劃般應該進程單位宏觀看布式系統由運行台機器進程組進程間採用TCP連接通信 使用TCP連接處兩點:容易定位布式系統服務間依賴關系要機器運行netstat -tpna|grep 能立刻列用某服務客戶端址(Foreign Address列)客戶端機器用netstat或lsof命令找哪進程發起連接TCP短連接UDP則具備特性二通接收發送隊列度較容易定位中國絡或程序故障運行候netstat列印Recv-QSend-Q都應該接近0或者0附近擺Recv-Q保持變或持續增加則通意味著服務進程處理速度變慢能發死鎖或阻塞Send-Q保持變或持續增加能伺服器太忙、及處理能中國絡間某路由器或交換機故障造丟包甚至伺服器掉線些素都能表現數據發送通持續監控Recv-QSend-Q能及早預警性能或用性故障服務端線程阻塞造Recv-Q客戶端Send-Q激增例: [cpp] view plain $netstat -tn Proto Recv-Q Send-Q Local Address Foreign tcp 78393 0 10.0.0.10:2000 10.0.0.10:39748 #服務端連接 tcp 0 132608 10.0.0.10:39748 10.0.0.10:2000 #客戶端連接 tcp 0 52 10.0.0.10:22 10.0.0.4:55572 五:線程伺服器適用場合 要台核機器提供種服務或執行任務用模式: a:運行單線程進程; b:運行線程進程; c:運行單線程進程; d:運行線程進程; 考慮場景:使用速率50MB/s數據壓縮庫進程創建銷毀銷800微秒線程創建銷毀銷50微秒何執行壓縮任務 要偶爾壓縮1GB文本文件預計運行間20s起進程做合理進程啟銷毀銷遠遠於實際任務耗 要經壓縮500kB文本數據預計運行間10ms每都起進程 似乎點浪費每單獨起線程做 要頻繁壓縮10kB文本數據預計運行間200微秒每起線程似 乎浪費直接前線程搞定用線程池每壓縮任務交給線程池避免阻塞前線程(特別要避免阻塞IO線程) 由見線程並萬靈丹(silver bullet) 1:必須使用單線程場合 據我所知兩種場合必須使用單線程: a:程序能fork(2); 實際編程應該保證單線程程序能進行fork(2)線程程序能調用fork(2)做遇麻煩: fork般能線程程序調用Linuxfork克隆前線程thread of control隆其線程fork除前線程外其線程都消失 造種危險局面其線程能處於臨界區內持某鎖突死亡再沒機解鎖進程試圖再同mutex加鎖立即死鎖fork進程相於處於signal handler(知道調用fork父進程線程調用函數信號發場景)能調用線程安全函數(除非重入)能調用非同步信號安全函數比fork進程能調用: mallocmalloc訪問全局狀態幾乎肯定加鎖; 任何能配或釋放內存函數比snprintf; 任何Pthreads函數; printf系列函數其線程能恰持stdout/stderr鎖; 除man 7 signal明確列信號安全函數外任何函數 線程調用fork唯安全做fork立即調用exec執行另程序徹底隔斷進程與父進程聯系 線程環境調用fork產進程進程內部存線程父進程調用fork線程副本 使用fork創建進程進程通繼承整址空間副本父進程繼承所互斥量、讀寫鎖條件變數狀態父進程某線程占鎖則進程同占些鎖問題進程並包含占鎖線程副本所進程沒辦知道占哪些鎖並且需要釋放哪些鎖 盡管Pthread提供pthread_atfork函數試圖繞問題使代碼變混亂《Programming With Posix Threads》書作者說:Avoid using fork in threaded code except where the child process will immediately exec a new program. b:限製程序CPU佔用率; 容易理解比8核伺服器單線程程序即便發busy-wait占滿1core其CPU使用率12.5%種壞情況系統87.5%計算資源供其服務進程使用 於些輔助性程序必須主要服務進程運行同台機器做單線程能避免搶奪系統計算資

閱讀全文

與linux多線程服務端編程pdf相關的資料

熱點內容
怎麼使用access的命令按鈕 瀏覽:897
有點錢app在哪裡下載 瀏覽:830
博途v15解壓後無法安裝 瀏覽:203
什麼是根伺服器主機 瀏覽:436
安卓手游怎麼申請退款 瀏覽:553
安卓系統如何分享網頁 瀏覽:278
ad如何編譯pcb工程 瀏覽:412
除了滴滴app哪裡還能用滴滴 瀏覽:399
截圖怎麼保存文件夾然後壓縮 瀏覽:8
幻影伺服器怎麼樣 瀏覽:27
具體哪些廣東公司招程序員 瀏覽:870
嵌入式編譯器教程 瀏覽:306
ssl數據加密傳輸 瀏覽:86
51單片機定時器方式2 瀏覽:331
命令行查看開機時間 瀏覽:813
python微博復雜網路分析 瀏覽:550
rf3148編程器 瀏覽:505
浙江標准網路伺服器機櫃雲主機 瀏覽:587
設置網路的伺服器地址 瀏覽:600
java圖形界面設計 瀏覽:751