服務器程序利用線程技術(shù)響應客戶請求已經(jīng)司空見慣,可能您認為這樣做效率已經(jīng)很高,但您有沒有想過優(yōu)化一下使用線程的方法。該文章將向您介紹服務器程序如何利用線程池來優(yōu)化性能并提供一個簡單的線程池實現(xiàn)。
線程池的技術(shù)背景
線程池的技術(shù)背景
在面向?qū)ο缶幊讨校瑒?chuàng)建和銷毀對象是很費時間的,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多資源。在Java中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷毀后進行垃圾回收。所以提高服務程序效率的一個手段就是盡可能減少創(chuàng)建和銷毀對象的次數(shù),特別是一些很耗資源的對象創(chuàng)建和銷毀。如何利用已有對象來服務就是一個需要解決的關(guān)鍵問題,其實這就是一些"池化資源"技術(shù)產(chǎn)生的原因。比如大家所熟悉的數(shù)據(jù)庫連接池正是遵循這一思想而產(chǎn)生的,本文將介紹的線程池技術(shù)同樣符合這一思想。
目前,一些著名的大公司都特別看好這項技術(shù),并早已經(jīng)在他們的產(chǎn)品中應用該技術(shù)。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。
現(xiàn)在您是否也想在服務器程序應用該項技術(shù)?
線程池技術(shù)如何提高服務器程序的性能
我所提到服務器程序是指能夠接受客戶請求并能處理請求的程序,而不只是指那些接受網(wǎng)絡客戶請求的網(wǎng)絡服務器程序。
多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:
假設在一臺服務器完成一項任務的時間為T
T1 創(chuàng)建線程的時間
T2 在線程中執(zhí)行任務的時間,包括線程間同步所需時間
T3 線程銷毀的時間
顯然T = T1+T2+T3。注意這是一個極度簡化的假設。
可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者并沒有注意到這一點,所以在程序中頻繁的創(chuàng)建或銷毀線程,這導致T1和T3在T中占有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優(yōu)點(并發(fā)性)。
線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時間的技術(shù),從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結(jié)束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。
線程池不僅調(diào)整T1,T3產(chǎn)生的時間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目。在看一個例子:
假設一個服務器一天要處理50000個請求,并且每個請求需要一個單獨的線程完成。我們比較利用線程池技術(shù)和不利于線程池技術(shù)的服務器處理這些請求時所產(chǎn)生的線程總數(shù)。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會超過線程池中線程的數(shù)目或者上限(以下簡稱線程池尺寸),而如果服務器不利用線程池來處理這些請求則線程總數(shù)為50000。一般線程池尺寸是遠小于50000。所以利用線程池的服務器程序不會為了創(chuàng)建50000而在處理請求時浪費時間,從而提高效率。
這些都是假設,不能充分說明問題,下面我將討論線程池的簡單實現(xiàn)并對該程序進行對比測試,以說明線程技術(shù)優(yōu)點及應用領(lǐng)域。
線程池的簡單實現(xiàn)及對比測試
一般一個簡單線程池至少包含下列組成部分。
線程池管理器(ThreadPoolManager):用于創(chuàng)建并管理線程池
工作線程(WorkThread): 線程池中線程
任務接口(Task):每個任務必須實現(xiàn)的接口,以供工作線程調(diào)度任務的執(zhí)行。
任務隊列:用于存放沒有處理的任務。提供一種緩沖機制。
線程池管理器至少有下列功能:創(chuàng)建線程池,銷毀線程池,添加新任務創(chuàng)建線程池的部分代碼如下:
??…
? ?? ???//create threads
? ?? ???synchronized(workThreadVector)
? ?? ???{
? ?? ?? ?? ?for(int j = 0; j < i; j++)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? threadNum++;
? ?? ?? ?? ?? ?WorkThread workThread = new WorkThread(taskVector, threadNum);
? ?? ?? ?? ?? ? workThreadVector.addElement(workThread);
? ?? ?? ?? ?}
? ?? ???}
…
注意同步workThreadVector并沒有降低效率,相反提高了效率,請參考Brian Goetz的文章。 銷毀線程池的部分代碼如下:
??…
? ?? ???while(!workThreadVector.isEmpty())
? ?? ???{
? ?? ???if(debugLevel > 2)
? ?? ?? ?System.out.println("stop:"+(i));
? ?? ?? ?i++;
? ?? ?? ?? ?try
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? WorkThread workThread = (WorkThread)workThreadVector.remove(0);
? ?? ?? ?? ?? ? workThread.closeThread();
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?}
? ?? ?? ?? ?catch(Exception exception)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? if(debugLevel > 2)
? ?? ?? ?? ?? ?? ???exception.printStackTrace();
? ?? ?? ?? ?}
? ?? ?? ?? ?break;
? ?? ???}
? ?…
? ?
添加新任務的部分代碼如下:
? ?…
? ?? ???synchronized(taskVector)
? ?? ???{
? ?? ?? ?? ?taskVector.addElement(taskObj);
? ?? ?? ?? ?taskVector.notifyAll();
? ?? ???}
? ?…
? ?
工作線程是一個可以循環(huán)執(zhí)行任務的線程,在沒有任務時將等待。由于代碼比較多在此不羅列.
任務接口是為所有任務提供統(tǒng)一的接口,以便工作線程處理。任務接口主要規(guī)定了任務的入口,任務執(zhí)行完后的收尾工作,任務的執(zhí)行狀態(tài)等。在文章結(jié)尾有相關(guān)代碼的下載。
以上所描述的線程池結(jié)構(gòu)很簡單,一些復雜的線程池結(jié)構(gòu)將不再此討論。
在下載代碼中有測試驅(qū)動程序(TestThreadPool),我利用這個測試程序的輸出數(shù)據(jù)統(tǒng)計出下列測試結(jié)果。測試有兩個參數(shù)要設置:
線程池中線程數(shù),即線程池尺寸。
要完成的任務數(shù)。
分別將一個參數(shù)固定,另一個參數(shù)變動以考察兩個參數(shù)所產(chǎn)生的不同結(jié)果。所用測試機器分別為普通PC機(Win2000 JDK1.3.1)和SUN服務器(Solaris Unix JDK1.3.1),機器配置在此不便指明。
表1:測試數(shù)據(jù)及對應結(jié)果
線程池尺寸 任務數(shù) 沒有應用線程池所用的時間(單位:毫秒,OS:win) 應用線程池所用的時間(單位:毫秒,OS:win) 沒有應用線程池所用的時間(單位:毫秒,OS:Solaris) 應用線程池所用的時間(單位:毫秒,OS:Solaris)
1 5000 3896 130 6513 327
2 5000 3455 151 6221 659
4 5000 3425 120 5448 433
8 5000 3475 160 5769 1478
16 5000 3505 211 5785 1970
32 5000 3455 251 6403 875
64 5000 3595 501 5182 1103
128 5000 3515 881 5154 405
256 5000 3495 3104 5502 1589
512 5000 3425 5488 5667 1262
16 1 20 0 22 3
16 2 20 20 21 13
16 4 20 10 27 10
16 8 20 20 22 24
16 16 30 20 29 48
16 32 40 20 46 108
16 64 60 20 72 199
16 128 110 20 148 335
16 256 201 20 252 132
16 512 411 40 522 382
16 1024 811 71 1233 610
16 2048 1552 80 2045 135
16 4096 2874 250 4828 787
圖1.線程池的尺寸的對服務器程序的性能影響
根據(jù)以上統(tǒng)計數(shù)據(jù)可得出下圖:
圖2.任務數(shù)對服務器程序的沖擊
數(shù)據(jù)分析如下:
圖1是改變線程池尺寸對服務器性能的影響,在該測試過程中,服務器的要完成的任務數(shù)固定為為5000。從圖1中可以看出合理配置線程池尺寸對于大量任務處理的效率有非常明顯的提高,但是一旦尺寸選擇不合理(過大或過小)就會嚴重降低影響服務器性能。理論上"過小"將出現(xiàn)任務不能及時處理的情況,但在圖表中顯示出某些小尺寸的線程池表現(xiàn)很好,這是因為測試驅(qū)動中有很多線程同步開銷,且這個開銷相對于完成單個任務的時間是不能忽略的。"過大"則會出現(xiàn)線程間同步開銷太大的問題,而且在線程間切換很耗CPU時間,在圖表顯示的很清楚。可見任何一個好技術(shù),如果濫用都會造成災難性后果。
圖2是用不同數(shù)量的任務來沖擊服務器程序,在該測試過程中,服務器線程池尺寸固定為16。可以看出線程池在處理少量任務時的優(yōu)勢不明顯。所以線程池技術(shù)有一定的適應范圍,關(guān)于適用范圍將在后面討論。但對于大量的任務的處理,線程池的優(yōu)勢表現(xiàn)非常卓越,服務器程序處理請求的時間雖然有波動,但是其平均值相對小多了。
值得注意的是測試方案中,統(tǒng)計任務的完成時間沒有包含了創(chuàng)建線程池的時間。在實際線程池工作時,即利用線程池處理任務時,創(chuàng)建線程池的時間是不必計算在內(nèi)的。
由于測試驅(qū)動程序有很多同步代碼,特別是等待線程執(zhí)行完畢的同步(代碼中為sleepToWait(long l)方法的調(diào)用),這些代碼降低了代碼執(zhí)行效率,這是測試驅(qū)動一個缺點,但這個測試驅(qū)動可以說明線程池相對于簡單使用線程的優(yōu)勢。
關(guān)于高級線程池的探討
簡單線程池存在一些問題,比如如果有大量的客戶要求服務器為其服務,但由于線程池的工作線程是有限的,服務器只能為部分客戶服務,其它客戶提交的任務,只能在任務隊列中等待處理。一些系統(tǒng)設計人員可能會不滿這種狀況,因為他們對服務器程序的響應時間要求比較嚴格,所以在系統(tǒng)設計時可能會懷疑線程池技術(shù)的可行性,但是線程池有相應的解決方案。調(diào)整優(yōu)化線程池尺寸是高級線程池要解決的一個問題。主要有下列解決方案:
方案一:動態(tài)增加工作線程
在一些高級線程池中一般提供一個可以動態(tài)改變的工作線程數(shù)目的功能,以適應突發(fā)性的請求。一旦請求變少了將逐步減少線程池中工作線程的數(shù)目。當然線程增加可以采用一種超前方式,即批量增加一批工作線程,而不是來一個請求才建立創(chuàng)建一個線程。批量創(chuàng)建是更加有效的方式。該方案還有應該限制線程池中工作線程數(shù)目的上限和下限。否則這種靈活的方式也就變成一種錯誤的方式或者災難,因為頻繁的創(chuàng)建線程或者短時間內(nèi)產(chǎn)生大量的線程將會背離使用線程池原始初衷--減少創(chuàng)建線程的次數(shù)。
舉例:Jini中的TaskManager,就是一個精巧線程池管理器,它是動態(tài)增加工作線程的。SQL Server采用單進程(Single Process)多線程(Multi-Thread)的系統(tǒng)結(jié)構(gòu),1024個數(shù)量的線程池,動態(tài)線程分配,理論上限32767。
方案二:優(yōu)化工作線程數(shù)目
如果不想在線程池應用復雜的策略來保證工作線程數(shù)滿足應用的要求,你就要根據(jù)統(tǒng)計學的原理來統(tǒng)計客戶的請求數(shù)目,比如高峰時段平均一秒鐘內(nèi)有多少任務要求處理,并根據(jù)系統(tǒng)的承受能力及客戶的忍受能力來平衡估計一個合理的線程池尺寸。線程池的尺寸確實很難確定,所以有時干脆用經(jīng)驗值。
舉例:在MTS中線程池的尺寸固定為100。
方案三:一個服務器提供多個線程池
在一些復雜的系統(tǒng)結(jié)構(gòu)會采用這個方案。這樣可以根據(jù)不同任務或者任務優(yōu)先級來采用不同線程池處理。
舉例:COM+用到了多個線程池。
這三種方案各有優(yōu)缺點。在不同應用中可能采用不同的方案或者干脆組合這三種方案來解決實際問題。
線程池技術(shù)適用范圍及應注意的問題
下面是我總結(jié)的一些線程池應用范圍,可能是不全面的。
線程池的應用范圍:
需要大量的線程來完成任務,且完成任務的時間比較短。
WEB服務器完成網(wǎng)頁請求這樣的任務,使用線程池技術(shù)是非常合適的。因為單個任務小,而任務數(shù)量巨大,你可以想象一個熱門網(wǎng)站的點擊次數(shù)。
但對于長時間的任務,比如一個Telnet連接請求,線程池的優(yōu)點就不明顯了。因為Telnet會話時間比線程的創(chuàng)建時間大多了。
對性能要求苛刻的應用,比如要求服務器迅速相應客戶請求。
接受突發(fā)性的大量請求,但不至于使服務器因此產(chǎn)生大量線程的應用。
突發(fā)性大量客戶請求,在沒有線程池情況下,將產(chǎn)生大量線程,雖然理論上大部分操作系統(tǒng)線程數(shù)目最大值不是問題,短時間內(nèi)產(chǎn)生大量線程可能使內(nèi)存到達極限,并出現(xiàn)"OutOfMemory"的錯誤。
結(jié)束語
本文只是簡單介紹線程池技術(shù)。可以看出線程池技術(shù)對于服務器程序的性能改善是顯著的。線程池技術(shù)在服務器領(lǐng)域有著廣泛的應用前景。希望這項技術(shù)能夠應用到您的多線程服務程序中。
現(xiàn)在您是否也想在服務器程序應用該項技術(shù)?
線程池技術(shù)如何提高服務器程序的性能
我所提到服務器程序是指能夠接受客戶請求并能處理請求的程序,而不只是指那些接受網(wǎng)絡客戶請求的網(wǎng)絡服務器程序。
多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:
假設在一臺服務器完成一項任務的時間為T
T1 創(chuàng)建線程的時間
T2 在線程中執(zhí)行任務的時間,包括線程間同步所需時間
T3 線程銷毀的時間
顯然T = T1+T2+T3。注意這是一個極度簡化的假設。
可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者并沒有注意到這一點,所以在程序中頻繁的創(chuàng)建或銷毀線程,這導致T1和T3在T中占有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優(yōu)點(并發(fā)性)。
線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時間的技術(shù),從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結(jié)束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。
線程池不僅調(diào)整T1,T3產(chǎn)生的時間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目。在看一個例子:
假設一個服務器一天要處理50000個請求,并且每個請求需要一個單獨的線程完成。我們比較利用線程池技術(shù)和不利于線程池技術(shù)的服務器處理這些請求時所產(chǎn)生的線程總數(shù)。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會超過線程池中線程的數(shù)目或者上限(以下簡稱線程池尺寸),而如果服務器不利用線程池來處理這些請求則線程總數(shù)為50000。一般線程池尺寸是遠小于50000。所以利用線程池的服務器程序不會為了創(chuàng)建50000而在處理請求時浪費時間,從而提高效率。
這些都是假設,不能充分說明問題,下面我將討論線程池的簡單實現(xiàn)并對該程序進行對比測試,以說明線程技術(shù)優(yōu)點及應用領(lǐng)域。
線程池的簡單實現(xiàn)及對比測試
一般一個簡單線程池至少包含下列組成部分。
線程池管理器(ThreadPoolManager):用于創(chuàng)建并管理線程池
工作線程(WorkThread): 線程池中線程
任務接口(Task):每個任務必須實現(xiàn)的接口,以供工作線程調(diào)度任務的執(zhí)行。
任務隊列:用于存放沒有處理的任務。提供一種緩沖機制。
線程池管理器至少有下列功能:創(chuàng)建線程池,銷毀線程池,添加新任務創(chuàng)建線程池的部分代碼如下:
??…
? ?? ???//create threads
? ?? ???synchronized(workThreadVector)
? ?? ???{
? ?? ?? ?? ?for(int j = 0; j < i; j++)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? threadNum++;
? ?? ?? ?? ?? ?WorkThread workThread = new WorkThread(taskVector, threadNum);
? ?? ?? ?? ?? ? workThreadVector.addElement(workThread);
? ?? ?? ?? ?}
? ?? ???}
…
注意同步workThreadVector并沒有降低效率,相反提高了效率,請參考Brian Goetz的文章。 銷毀線程池的部分代碼如下:
??…
? ?? ???while(!workThreadVector.isEmpty())
? ?? ???{
? ?? ???if(debugLevel > 2)
? ?? ?? ?System.out.println("stop:"+(i));
? ?? ?? ?i++;
? ?? ?? ?? ?try
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? WorkThread workThread = (WorkThread)workThreadVector.remove(0);
? ?? ?? ?? ?? ? workThread.closeThread();
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?}
? ?? ?? ?? ?catch(Exception exception)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? if(debugLevel > 2)
? ?? ?? ?? ?? ?? ???exception.printStackTrace();
? ?? ?? ?? ?}
? ?? ?? ?? ?break;
? ?? ???}
? ?…
? ?
添加新任務的部分代碼如下:
? ?…
? ?? ???synchronized(taskVector)
? ?? ???{
? ?? ?? ?? ?taskVector.addElement(taskObj);
? ?? ?? ?? ?taskVector.notifyAll();
? ?? ???}
? ?…
? ?
工作線程是一個可以循環(huán)執(zhí)行任務的線程,在沒有任務時將等待。由于代碼比較多在此不羅列.
任務接口是為所有任務提供統(tǒng)一的接口,以便工作線程處理。任務接口主要規(guī)定了任務的入口,任務執(zhí)行完后的收尾工作,任務的執(zhí)行狀態(tài)等。在文章結(jié)尾有相關(guān)代碼的下載。
以上所描述的線程池結(jié)構(gòu)很簡單,一些復雜的線程池結(jié)構(gòu)將不再此討論。
在下載代碼中有測試驅(qū)動程序(TestThreadPool),我利用這個測試程序的輸出數(shù)據(jù)統(tǒng)計出下列測試結(jié)果。測試有兩個參數(shù)要設置:
線程池中線程數(shù),即線程池尺寸。
要完成的任務數(shù)。
分別將一個參數(shù)固定,另一個參數(shù)變動以考察兩個參數(shù)所產(chǎn)生的不同結(jié)果。所用測試機器分別為普通PC機(Win2000 JDK1.3.1)和SUN服務器(Solaris Unix JDK1.3.1),機器配置在此不便指明。
表1:測試數(shù)據(jù)及對應結(jié)果
線程池尺寸 任務數(shù) 沒有應用線程池所用的時間(單位:毫秒,OS:win) 應用線程池所用的時間(單位:毫秒,OS:win) 沒有應用線程池所用的時間(單位:毫秒,OS:Solaris) 應用線程池所用的時間(單位:毫秒,OS:Solaris)
1 5000 3896 130 6513 327
2 5000 3455 151 6221 659
4 5000 3425 120 5448 433
8 5000 3475 160 5769 1478
16 5000 3505 211 5785 1970
32 5000 3455 251 6403 875
64 5000 3595 501 5182 1103
128 5000 3515 881 5154 405
256 5000 3495 3104 5502 1589
512 5000 3425 5488 5667 1262
16 1 20 0 22 3
16 2 20 20 21 13
16 4 20 10 27 10
16 8 20 20 22 24
16 16 30 20 29 48
16 32 40 20 46 108
16 64 60 20 72 199
16 128 110 20 148 335
16 256 201 20 252 132
16 512 411 40 522 382
16 1024 811 71 1233 610
16 2048 1552 80 2045 135
16 4096 2874 250 4828 787
圖1.線程池的尺寸的對服務器程序的性能影響
根據(jù)以上統(tǒng)計數(shù)據(jù)可得出下圖:
圖2.任務數(shù)對服務器程序的沖擊
數(shù)據(jù)分析如下:
圖1是改變線程池尺寸對服務器性能的影響,在該測試過程中,服務器的要完成的任務數(shù)固定為為5000。從圖1中可以看出合理配置線程池尺寸對于大量任務處理的效率有非常明顯的提高,但是一旦尺寸選擇不合理(過大或過小)就會嚴重降低影響服務器性能。理論上"過小"將出現(xiàn)任務不能及時處理的情況,但在圖表中顯示出某些小尺寸的線程池表現(xiàn)很好,這是因為測試驅(qū)動中有很多線程同步開銷,且這個開銷相對于完成單個任務的時間是不能忽略的。"過大"則會出現(xiàn)線程間同步開銷太大的問題,而且在線程間切換很耗CPU時間,在圖表顯示的很清楚。可見任何一個好技術(shù),如果濫用都會造成災難性后果。
圖2是用不同數(shù)量的任務來沖擊服務器程序,在該測試過程中,服務器線程池尺寸固定為16。可以看出線程池在處理少量任務時的優(yōu)勢不明顯。所以線程池技術(shù)有一定的適應范圍,關(guān)于適用范圍將在后面討論。但對于大量的任務的處理,線程池的優(yōu)勢表現(xiàn)非常卓越,服務器程序處理請求的時間雖然有波動,但是其平均值相對小多了。
值得注意的是測試方案中,統(tǒng)計任務的完成時間沒有包含了創(chuàng)建線程池的時間。在實際線程池工作時,即利用線程池處理任務時,創(chuàng)建線程池的時間是不必計算在內(nèi)的。
由于測試驅(qū)動程序有很多同步代碼,特別是等待線程執(zhí)行完畢的同步(代碼中為sleepToWait(long l)方法的調(diào)用),這些代碼降低了代碼執(zhí)行效率,這是測試驅(qū)動一個缺點,但這個測試驅(qū)動可以說明線程池相對于簡單使用線程的優(yōu)勢。
關(guān)于高級線程池的探討
簡單線程池存在一些問題,比如如果有大量的客戶要求服務器為其服務,但由于線程池的工作線程是有限的,服務器只能為部分客戶服務,其它客戶提交的任務,只能在任務隊列中等待處理。一些系統(tǒng)設計人員可能會不滿這種狀況,因為他們對服務器程序的響應時間要求比較嚴格,所以在系統(tǒng)設計時可能會懷疑線程池技術(shù)的可行性,但是線程池有相應的解決方案。調(diào)整優(yōu)化線程池尺寸是高級線程池要解決的一個問題。主要有下列解決方案:
方案一:動態(tài)增加工作線程
在一些高級線程池中一般提供一個可以動態(tài)改變的工作線程數(shù)目的功能,以適應突發(fā)性的請求。一旦請求變少了將逐步減少線程池中工作線程的數(shù)目。當然線程增加可以采用一種超前方式,即批量增加一批工作線程,而不是來一個請求才建立創(chuàng)建一個線程。批量創(chuàng)建是更加有效的方式。該方案還有應該限制線程池中工作線程數(shù)目的上限和下限。否則這種靈活的方式也就變成一種錯誤的方式或者災難,因為頻繁的創(chuàng)建線程或者短時間內(nèi)產(chǎn)生大量的線程將會背離使用線程池原始初衷--減少創(chuàng)建線程的次數(shù)。
舉例:Jini中的TaskManager,就是一個精巧線程池管理器,它是動態(tài)增加工作線程的。SQL Server采用單進程(Single Process)多線程(Multi-Thread)的系統(tǒng)結(jié)構(gòu),1024個數(shù)量的線程池,動態(tài)線程分配,理論上限32767。
方案二:優(yōu)化工作線程數(shù)目
如果不想在線程池應用復雜的策略來保證工作線程數(shù)滿足應用的要求,你就要根據(jù)統(tǒng)計學的原理來統(tǒng)計客戶的請求數(shù)目,比如高峰時段平均一秒鐘內(nèi)有多少任務要求處理,并根據(jù)系統(tǒng)的承受能力及客戶的忍受能力來平衡估計一個合理的線程池尺寸。線程池的尺寸確實很難確定,所以有時干脆用經(jīng)驗值。
舉例:在MTS中線程池的尺寸固定為100。
方案三:一個服務器提供多個線程池
在一些復雜的系統(tǒng)結(jié)構(gòu)會采用這個方案。這樣可以根據(jù)不同任務或者任務優(yōu)先級來采用不同線程池處理。
舉例:COM+用到了多個線程池。
這三種方案各有優(yōu)缺點。在不同應用中可能采用不同的方案或者干脆組合這三種方案來解決實際問題。
線程池技術(shù)適用范圍及應注意的問題
下面是我總結(jié)的一些線程池應用范圍,可能是不全面的。
線程池的應用范圍:
需要大量的線程來完成任務,且完成任務的時間比較短。
WEB服務器完成網(wǎng)頁請求這樣的任務,使用線程池技術(shù)是非常合適的。因為單個任務小,而任務數(shù)量巨大,你可以想象一個熱門網(wǎng)站的點擊次數(shù)。
但對于長時間的任務,比如一個Telnet連接請求,線程池的優(yōu)點就不明顯了。因為Telnet會話時間比線程的創(chuàng)建時間大多了。
對性能要求苛刻的應用,比如要求服務器迅速相應客戶請求。
接受突發(fā)性的大量請求,但不至于使服務器因此產(chǎn)生大量線程的應用。
突發(fā)性大量客戶請求,在沒有線程池情況下,將產(chǎn)生大量線程,雖然理論上大部分操作系統(tǒng)線程數(shù)目最大值不是問題,短時間內(nèi)產(chǎn)生大量線程可能使內(nèi)存到達極限,并出現(xiàn)"OutOfMemory"的錯誤。
結(jié)束語
本文只是簡單介紹線程池技術(shù)。可以看出線程池技術(shù)對于服務器程序的性能改善是顯著的。線程池技術(shù)在服務器領(lǐng)域有著廣泛的應用前景。希望這項技術(shù)能夠應用到您的多線程服務程序中。