posted @ 2015-01-13 20:35 鑫龍 閱讀(441) | 評論 (0) | 編輯 收藏
posted @ 2014-09-29 21:31 鑫龍 閱讀(7734) | 評論 (0) | 編輯 收藏
首先下載源碼包
http://www.cmake.org/cmake/resources/software.html
這里下載的是cmake-2.8.9.tar.gz
隨便找個目錄解壓縮
1 |
tar -xzvf cmake-2.8.9.tar.gz |
2 |
cd cmake-cmake-2.8.9 |
依次執行:
1 |
./bootstrap |
2 |
make |
3 |
make install |
cmake 會默認安裝在 /usr/local/bin 下面
要改變安裝路徑,在bootstrap命令中加入'--prefix=PATH'選項。
二、linux下安裝boost
linux平臺下要編譯安裝除gcc和gcc-c++之外,還需要兩個開發庫:bzip2-devel 和python-devel,因此在安裝前應該先保證這兩個庫已經安裝:
然后是去官網下載源碼包,地址#yum install gcc gcc-c++ bzip2 bzip2-devel bzip2-libs python-devel -y
下載,解壓,按照如下步驟:
進入boost_1_50_0目錄:#tar xvzf boost_1_50_0.tar.gz
#cd boost_1_50_0
然后是編譯安裝,boost源碼包中有配置腳本,直接用就可以:
接下來就是編譯,重點關注是否編譯成功:#sh ./bootstrap.sh
Building Boost.Build engine with toolset gcc... tools/build/v2/engine/bin.linuxx86_64/b2
Detecting Python version... 2.6
Detecting Python root... /usr
Unicode/ICU support for Boost.Regex?... not found.
Generating Boost.Build configuration in project-config.jam...
Bootstrapping is done. To build, run:
./b2
To adjust configuration, edit 'project-config.jam'.
Further information:
- Command line help:
./b2 --help
- Getting started guide:
http://www.boost.org/more/getting_started/unix-variants.html
- Boost.Build documentation:
http://www.boost.org/boost-build2/doc/html/index.html
然后就是漫長的等待,如果最后出現:#./b2
表示編譯成功,如果沒有成功,就需要回查看哪里出現error,再安裝相應的庫,The Boost C++ Libraries were successfully built!
The following directory should be added to compiler include paths:
/home/gang/BAK/boost_1_50_0
The following directory should be added to linker library paths:
/home/gang/BAK/boost_1_50_0/stage/lib
最后就是安裝:
安裝后的頭文件在/usr/local/include/boost里面,而相應的庫在/usr/local/lib/libboost_*#./b2 install --prefix=/usr/local
posted @ 2014-09-23 09:29 鑫龍 閱讀(1101) | 評論 (0) | 編輯 收藏
轉自:http://www.cnblogs.com/jiqing9006/archive/2012/09/11/2679831.html
冒泡事件就是點擊子節點,會向上觸發父節點,祖先節點的點擊事件。
下面是html代碼部分:
<body> <div id="content"> 外層div元素 <span>內層span元素</span> 外層div元素 </div> <div id="msg"></div> </body>
對應的jQuery代碼如下:
<script type="text/javascript"> $(function(){ // 為span元素綁定click事件 $('span').bind("click",function(){ var txt = $('#msg').html() + "<p>內層span元素被點擊.<p/>";//獲取html信息 $('#msg').html(txt);// 設置html信息 }); // 為div元素綁定click事件 $('#content').bind("click",function(){ var txt = $('#msg').html() + "<p>外層div元素被點擊.<p/>"; $('#msg').html(txt); }); // 為body元素綁定click事件 $("body").bind("click",function(){ var txt = $('#msg').html() + "<p>body元素被點擊.<p/>"; $('#msg').html(txt); }); }) </script>
當點擊span時,會觸發div與body 的點擊事件。點擊div時會觸發body的點擊事件。
如何防止這種冒泡事件發生呢?
修改如下:
<script type="text/javascript"> $(function(){ // 為span元素綁定click事件 $('span').bind("click",function(event){ var txt = $('#msg').html() + "<p>內層span元素被點擊.<p/>"; $('#msg').html(txt); event.stopPropagation(); // 阻止事件冒泡 }); // 為div元素綁定click事件 $('#content').bind("click",function(event){ var txt = $('#msg').html() + "<p>外層div元素被點擊.<p/>"; $('#msg').html(txt); event.stopPropagation(); // 阻止事件冒泡 }); // 為body元素綁定click事件 $("body").bind("click",function(){ var txt = $('#msg').html() + "<p>body元素被點擊.<p/>"; $('#msg').html(txt); }); }) </script>
event.stopPropagation(); // 阻止事件冒泡
有時候點擊提交按鈕會有一些默認事件。比如跳轉到別的界面。但是如果沒有通過驗證的話,就不應該跳轉。這時候可以通過設置event.preventDefault(); //阻止默認行為 ( 表單提交 )。
下面是案例:
<script type="text/javascript"> $(function(){ $("#sub").bind("click",function(event){ var username = $("#username").val(); //獲取元素的值,val() 方法返回或設置被選元素的值。 if(username==""){ //判斷值是否為空 $("#msg").html("<p>文本框的值不能為空.</p>"); //提示信息 event.preventDefault(); //阻止默認行為 ( 表單提交 ) } }) }) </script>
html部分:
<body> <form action="test.html"> 用戶名:<input type="text" id="username" /> <br/> <input type="submit" value="提交" id="sub"/> </form> <div id="msg"></div> </body>
還有一種防止默認行為的方法就是return false。效果一樣。
代碼如下:
<script type="text/javascript"> $(function(){ $("#sub").bind("click",function(event){ var username = $("#username").val(); //獲取元素的值 if(username==""){ //判斷值是否為空 $("#msg").html("<p>文本框的值不能為空.</p>"); //提示信息 return false; } }) }) </script>
同理,上面的冒泡事件也可以通過return false來處理。
<script type="text/javascript"> $(function(){ // 為span元素綁定click事件 $('span').bind("click",function(event){ var txt = $('#msg').html() + "<p>內層span元素被點擊.<p/>"; $('#msg').html(txt); return false; }); // 為div元素綁定click事件 $('#content').bind("click",function(event){ var txt = $('#msg').html() + "<p>外層div元素被點擊.<p/>"; $('#msg').html(txt); return false; }); // 為body元素綁定click事件 $("body").bind("click",function(){ var txt = $('#msg').html() + "<p>body元素被點擊.<p/>"; $('#msg').html(txt); }); }) </script>
posted @ 2014-09-17 14:22 鑫龍 閱讀(292) | 評論 (0) | 編輯 收藏
轉自:http://www.cnblogs.com/jiayy/p/3246167.html
在看多核編程相關論文時,往往一個并發算法會說自己是wait-free的或者lock-free的,或者是 non-blocking 的,這些專有詞匯其實表示的是并發的程度,或者說并發的級別。并發級別的理解是閱讀各種并發算法設計論文以及并發數據結構實現的必備基礎。
1.1 Wait-freedom 無等待并發
Wait-freedom 指的是每一個線程都一直運行下去而無須等待外部條件,整個流程中任何操作都能在一個有限的步驟內完成,這是最高的并發級別,沒有任何阻塞。
結合之前原子操作部分的知識,可以簡單認為能夠直接調用一個原子操作實現的算法或程序就屬于Wait-free,比如下面的 increment_reference_counter 函數就是wait-free的,它封裝了atomic_increment這個原子自增原語,多個線程可以同時調用這個函數對同一個內存變量進行自增,而無須任何阻塞(其實也是有阻塞的,是總線鎖級別)
與此做對比,CAS類的調用就不是wait-free的,注意wait-free的原語都不能包含內部循環,CAS原語使用時通常包含在“循環直到成功”的循環內部。
void increment_reference_counter(rc_base* obj)
{
atomic_increment(obj->rc);
}
1.2 Lock-freedom 無鎖并發
Lock-freedom 指的是整個系統作為一個整體一直運行下去,系統內部單個線程某段時間內可能會饑餓,這是比wait-freedom弱的并發級別,但系統整體上看依然是沒有阻塞的。所有wait-free的算法顯然都滿足lock-free的要求。
Lock-free算法通常可以通過同步原語 CAS實現。
void stack_push(stack* s, node* n)
{
node* head;
do
{
head = s->head;
n->next = head;
}
while ( ! atomic_compare_exchange(s->head, head, n));
}
多個線程同時調用上述函數,理論上某個線程可以一直困在循環內部,但一旦有一個線程原子操作失敗而返回循環,意味著有其他線程成功執行了原子操作而退出循環,從而保證系統整體是沒有阻塞的。
其實前面的原子自增函數也可以用下面的原語實現,在這種實現里,不再是所有線程都無阻塞了,某些線程可能會因為CAS失敗而回繞若干次循環。
void increment_reference_counter(rc_base* obj)
{
Int rc;
Do {
rc = obj->rc;
} while(!atomic_compare_exchange(obj->rc,rc,rc+1));
}
1.3 Obstruction-freedom 無阻塞并發
Obstruction-free 是指在任何時間點,一個孤立運行線程的每一個操作可以在有限步之內結束。只要沒有競爭,線程就可以持續運行,一旦共享數據被修改,Obstruction-free 要求中止已經完成的部分操作,并進行回滾,obstruction-free 是并發級別更低的非阻塞并發,該算法在不出現沖突性操作的情況下提供單線程式的執行進度保證,所有 Lock-Free 的算法都是 Obstruction-free 的。
1.4 Blocking algoithms 阻塞并發
阻塞類的算法是并發級別最低的同步算法,它一般需要產生阻塞。可以簡單認為基于鎖的實現是blocking的算法。詳細參考第五章
上述幾種并發級別可以使用下圖描述:
藍色是阻塞的算法,綠色是非阻塞算法,金字塔越上方,并發級別越高,性能越好,右邊的金字塔是實現工具(原子操作、鎖、互斥體等)
posted @ 2014-09-12 13:05 鑫龍 閱讀(434) | 評論 (0) | 編輯 收藏
以前一直不明白lock free是什么,后來發現原來是完全理解錯了概念,lock free看到大家有的翻譯為無鎖,有的翻譯為鎖無關,其實用不用鎖和lock free是不相關的,用了鎖也可能是lock free,而不用鎖有可能不是lock free。
一個lock free的解釋是
一個“鎖無關”的程序能夠確保執行它的所有線程中至少有一個能夠繼續往下執行。
其實看我們那副圖就是說你的各個線程不會互相阻塞,那么你的程序才能成為lock free的。像我們平常用的互斥鎖,當有線程獲得鎖,其他線程就被阻塞掉了,這里的問題就是如果獲得鎖的線程掛掉了,而且鎖也沒有釋放,那么整個程序其實就被block在那了,而如果程序是lock free的那么即使有線程掛掉,也不影響整個程序繼續向下進行,也就是系統在整體上而言是一直前進的。
那么,不用鎖就是lock free的嗎,一開始就提到了,不用鎖也可能不是lock free的,舉個例子
-
while (x == 0) {
-
x = 1-x;
-
}
在這里如果兩個線程同時執行,可能同時進入while循環,然后x兩次改變值之后,依然是0,那么兩個線程就會一直互相在這里阻塞掉了,所以這里雖然沒有鎖,依然不是lock free的。
現在大家寫lock free的時候一般都會使用CAS(compare and set)操作來寫,因為現在很多的cpu都是支持CAS操作并作為原子操作來處理的,CAS操作一般是這樣的
-
bool compare_and_swap (int *oldval, int *dest, int newval) {
-
if (*oldval == *dest) {
-
*dest = newval;
-
return true;
-
}
-
return false;
-
}
其實這樣一個操作和樂觀鎖很像,并且操作簡單,相應的比互斥鎖的代價要小。所以現在大家都是喜歡用lock free的技術來提高系統的performance。
最后如果大家對于如何編寫lock free的數據結構感興趣的話,可以參考我后面給出的鏈接。
一種高效無鎖內存隊列的實現
無鎖隊列的實現
鎖無關的(Lock-Free)數據結構
An Introduction to Lock-Free Programming
posted @ 2014-09-03 21:11 鑫龍 閱讀(3252) | 評論 (0) | 編輯 收藏
轉自:http://zhucuicui.96986.blog.163.com/blog/static/5833370220136219016445/
建立socket后默認connect()函數為阻塞連接狀態,在大多數實現中,connect的超時時間在75s至幾分鐘之間,想要縮短超時時間,可解決問題的兩種方法:方法一、將socket句柄設置為非阻塞狀態,方法二、采用信號處理函數設置阻塞超時控制。
在一個TCP套接口被設置為非阻塞之后調用connect,connect會立即返回EINPROGRESS錯誤,表示連接操作正在進行中,但是仍未完成;同時TCP的三路握手操作繼續進行;在這之后,我們可以調用select來檢查這個鏈接是否建立成功;非阻塞connect有三種用途:
1.我們可以在三路握手的同時做一些其它的處理.connect操作要花一個往返時間完成,而且可以是在任何地方,從幾個毫秒的局域網到幾百毫秒或幾秒的廣域網.在這段時間內我們可能有一些其他的處理想要執行;
2.可以用這種技術同時建立多個連接.在Web瀏覽器中很普遍;
3.由于我們使用select來等待連接的完成,因此我們可以給select設置一個時間限制,從而縮短connect的超時時間.在大多數實現中,connect的超時時間在75秒到幾分鐘之間.有時候應用程序想要一個更短的超時時間,使用非阻塞connect就是一種方法;
非阻塞connect聽起來雖然簡單,但是仍然有一些細節問題要處理:
1.即使套接口是非阻塞的,如果連接的服務器在同一臺主機上,那么在調用connect建立連接時,連接通常會立即建立成功.我們必須處理這種情況;
2.源自Berkeley的實現(和Posix.1g)有兩條與select和非阻塞IO相關的規則:
A:當連接建立成功時,套接口描述符變成可寫;
B:當連接出錯時,套接口描述符變成既可讀又可寫;
注意:當一個套接口出錯時,它會被select調用標記為既可讀又可寫;
非阻塞connect有這么多好處,但是處理非阻塞connect時會遇到很多可移植性問題;
處理非阻塞connect的步驟:
第一步:創建socket,返回套接口描述符;
第二步:調用fcntl把套接口描述符設置成非阻塞;
第三步:調用connect開始建立連接;
第四步:判斷連接是否成功建立;
A:如果connect返回0,表示連接簡稱成功(服務器可客戶端在同一臺機器上時就有可能發生這種情況);
B:調用select來等待連接建立成功完成;
如果select返回0,則表示建立連接超時;我們返回超時錯誤給用戶,同時關閉連接,以防止三路握手操作繼續進行下去;
如果select返回大于0的值,則需要檢查套接口描述符是否可讀或可寫;如果套接口描述符可讀或可寫,則我們可以通過調用getsockopt來得到套接口上待處理的錯誤(SO_ERROR),如果連接建立成功,這個錯誤值將是0,如果建立連接時遇到錯誤,則這個值是連接錯誤所對應的errno值(比如:ECONNREFUSED,ETIMEDOUT等).
"讀取套接口上的錯誤"是遇到的第一個可移植性問題;如果出現問題,getsockopt源自Berkeley的實現是返回0,等待處理的錯誤在變量errno中返回;但是Solaris會讓getsockopt返回-1,errno置為待處理的錯誤;我們對這兩種情況都要處理;
這樣,在處理非阻塞connect時,在不同的套接口實現的平臺中存在的移植性問題,首先,有可能在調用select之前,連接就已經建立成功,而且對方的數據已經到來.在這種情況下,連接成功時套接口將既可讀又可寫.這和連接失敗時是一樣的.這個時候我們還得通過getsockopt來讀取錯誤值;這是第二個可移植性問題;
移植性問題總結:
1.對于出錯的套接口描述符,getsockopt的返回值源自Berkeley的實現是返回0,待處理的錯誤值存儲在errno中;而源自Solaris的實現是返回0,待處理的錯誤存儲在errno中;(套接口描述符出錯時調用getsockopt的返回值不可移植)
2.有可能在調用select之前,連接就已經建立成功,而且對方的數據已經到來,在這種情況下,套接口描述符是既可讀又可寫;這與套接口描述符出錯時是一樣的;(怎樣判斷連接是否建立成功的條件不可移植)
這樣的話,在我們判斷連接是否建立成功的條件不唯一時,我們可以有以下的方法來解決這個問題:
1.調用getpeername代替getsockopt.如果調用getpeername失敗,getpeername返回ENOTCONN,表示連接建立失敗,我們必須以SO_ERROR調用getsockopt得到套接口描述符上的待處理錯誤;
2.調用read,讀取長度為0字節的數據.如果read調用失敗,則表示連接建立失敗,而且read返回的errno指明了連接失敗的原因.如果連接建立成功,read應該返回0;
3.再調用一次connect.它應該失敗,如果錯誤errno是EISCONN,就表示套接口已經建立,而且第一次連接是成功的;否則,連接就是失敗的;
被中斷的connect:
如果在一個阻塞式套接口上調用connect,在TCP的三路握手操作完成之前被中斷了,比如說,被捕獲的信號中斷,將會發生什么呢?假定connect不會自動重啟,它將返回EINTR.那么,這個時候,我們就不能再調用connect等待連接建立完成了,如果再次調用connect來等待連接建立完成的話,connect將會返回錯誤值EADDRINUSE.在這種情況下,應該做的是調用select,就像在非阻塞式connect中所做的一樣.然后,select在連接建立成功(使套接口描述符可寫)或連接建立失敗(使套接口描述符既可讀又可寫)時返回;
方法二、定義信號處理函數:
- sigset(SIGALRM, u_alarm_handler);
- alarm(2);
- code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
- alarm(0);
- sigrelse(SIGALRM);
首先定義一個中斷信號處理函數u_alarm_handler,用于超時后的報警處理,然后定義一個2秒的定時器,執行connect,當系統connect成功,則系統正常執行下去;如果connect不成功阻塞在這里,則超過定義的2秒后,系統會產生一個信號,觸發執行u_alarm_handler函數, 當執行完u_alarm_handler后,程序將繼續從connect的下面一行執行下去。
其中,處理函數可以如下定義,也可以加入更多的錯誤處理。
- void u_alarm_handler()
- {
- }
posted @ 2014-08-23 17:01 鑫龍 閱讀(1671) | 評論 (0) | 編輯 收藏
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
“據說”驚群問題已經是一個很古老的問題了,并且在大多數系統中已經得到有效解決,但對我來說,仍舊是一個比較新的概念,因此有必要記錄一下。
什么是驚群
舉一個很簡單的例子,當你往一群鴿子中間扔一塊食物,雖然最終只有一個鴿子搶到食物,但所有鴿子都會被驚動來爭奪,沒有搶到食物的鴿子只好回去繼續睡覺, 等待下一塊食物到來。這樣,每扔一塊食物,都會驚動所有的鴿子,即為驚群。對于操作系統來說,多個進程/線程在等待同一資源是,也會產生類似的效果,其結 果就是每當資源可用,所有的進程/線程都來競爭資源,造成的后果:
1)系統對用戶進程/線程頻繁的做無效的調度、上下文切換,系統系能大打折扣。
2)為了確保只有一個線程得到資源,用戶必須對資源操作進行加鎖保護,進一步加大了系統開銷。
最常見的例子就是對于socket描述符的accept操作,當多個用戶進程/線程監聽在同一個端口上時,由于實際只可能accept一次,因此就會產生驚群現象,當然前面已經說過了,這個問題是一個古老的問題,新的操作系統內核已經解決了這一問題。
linux內核解決驚群問題的方法
對于一些已知的驚群問題,內核開發者增加了一個“互斥等待”選項。一個互斥等待的行為與睡眠基本類似,主要的不同點在于:
1)當一個等待隊列入口有 WQ_FLAG_EXCLUSEVE 標志置位, 它被添加到等待隊列的尾部. 沒有這個標志的入口項, 相反, 添加到開始.
2)當 wake_up 被在一個等待隊列上調用時, 它在喚醒第一個有 WQ_FLAG_EXCLUSIVE 標志的進程后停止。
也就是說,對于互斥等待的行為,比如如對一個listen后的socket描述符,多線程阻塞accept時,系統內核只會喚醒所有正在等待此時間的隊列 的第一個,隊列中的其他人則繼續等待下一次事件的發生,這樣就避免的多個線程同時監聽同一個socket描述符時的驚群問題。
根據以上背景信息,我們來比較一下常見的Server端設計方案。
方案2:主線程負責監聽,通過線程池方式處理連接。(通常的方法)
方案3:主線程負責監聽,客戶端連接上來后由主線程分配實際的端口,客戶端根據此端口重新連接,然后處理數據。
先考慮客戶端單連接的情況:
方案1:每當有新的連接到來時,系統內核會從隊列中以FIFO的方式選擇一個監聽線程來服務此連接,因此可以充分發揮系統的系能并且多線程負載均衡。對于單連接的場景,這種方案無疑是非常優越的。遺憾的是,對于select、epoll,內核目前無法解決驚群問題。(nginx對于驚群問題的解決方法)
方案2:由于只有一個線程在監聽,其瞬時的并發處理連接請求的能力必然不如多線程。同時,需要對線程池做調度管理,必然涉及資源共享訪問,相對于方案一來說管理成本要增加不少,代碼復雜度提高,性能也有所下降。
方案3:與方案2有不少類似的地方,其優勢是不需要做線程調度。缺點是增加了主線程的負擔,除了接收連接外還需要發送數據,而且需要兩次連接,孰優孰劣,有待測試。
再考慮客戶端多連接的情況:
對于數據傳輸類的應用,為了充分利用帶寬,往往會開啟多個連接來傳輸數據,連接之間的數據有相互依賴性,因此Server端要想很好的維護這種依賴性,把同一個客戶端的所有連接放在一個線程中處理是非常有必要的。
A、同一客戶端在一個線程中處理
方案1:如果沒有更底層的解決方案的話,Server則需要維護一個全局列表,來記錄當前連接請求該由哪個線程處理。多線程需要同時競爭一個全局資源,似乎有些不妙。
方案2:主線程負責監聽并分發,因此與單連接相比沒有帶來額外的性能開銷。僅僅會造成主線程忙于更多的連接請求。
方案3:較單線程來說,主線程工作量沒有任何增加,由于多連接而造成的額外開銷由實際工作線程分擔,因此對于這種場景,方案3似乎是最佳選擇。
B、同一客戶端在不同線程中處理
方案1:同樣需要競爭資源。
方案2:沒理由。
方案3:不可能。
另外:
讀《UNIX網絡編程》第二版的第一卷時,發現作者在第27章“客戶-服務器程序其它設計方法”中的27.6節“TCP預先派生子進程服務器程序,accept無上鎖保護”中提到了一種由子進程去競爭客戶端連接的設計方法,用偽碼描述如下:
服務器主進程:
|
|
服務器服務子進程:
|
|
初識上述代碼,真有眼前一亮的感覺,也正如作者所說,以上代碼確實很少見(反正我讀此書之前是確實沒見過)。作者真是構思精巧,巧妙地繞過了常見的預先創建 子進程的多進程服務器當主服務進程接收到新的連接必須想辦法將這個連接傳遞給服務子進程的“陷阱”,上述代碼通過共享的傾聽套接字,由子進程主動地去向內 核“索要”連接套接字,從而避免了用UNIX域套接字傳遞文件描述符的“淫技”。
不過,當接著往下讀的時候,作者談到了“驚群” (Thundering herd)問題。所謂的“驚群”就是,當很多進程都阻塞在accept系統調用的時候,即使只有一個新的連接達到,內核也會喚醒所有阻塞在accept上 的進程,這將給系統帶來非常大的“震顫”,降低系統性能。
除了這個問題,accept還必須是原子操作。為此,作者在接下來的27.7節講述了加了互斥鎖的版本:
|
|
原子操作的問題算是解決了,那么“驚群”呢?文中只是提到在Solaris系統上當子進程數由75變成90后,CPU時間顯著增加,并且作者認為這是因為進 程過多,導致內存互換。對“驚群”問題回答地十分含糊。通過比較書中圖27.2的第4列和第7列的內容,我們可以肯定“真兇”絕對不是“內存對換”。
“元兇”到底是誰?
仔細分析一下,加鎖真的有助于“驚群”問題么?不錯,確實在同一時間只有一個子進程在調用accept,其它子進程都阻塞在了lock語句,但是,當 accept返回并unlock之后呢?unlock肯定是要喚醒阻塞在這個鎖上的進程的,不過誰都沒有規定是喚醒一個還是喚醒多個。所以,潛在的“驚 群”問題還是存在,只不過換了個地方,換了個形式。而造成Solaris性能驟降的“罪魁禍首”很有可能就是“驚群”問題。
崩潰了!這么說所有的鎖都有可能產生驚群問題了?
似乎真的是這樣,所以減少鎖的使用很重要。特別是在競爭比較激烈的地方。
作者在27.9節所實現的“傳遞文件描述符”版本的服務器就有效地克服了“驚群”問題,在現實的服務器實現中,最常用的也是此節所提到的基于“分配”形式。
把“競爭”換成“分配”是避免“驚群”問題的有效方法,但是也不要忽視“分配”的“均衡”問題,不然后果可能更加嚴重哦!
posted @ 2014-08-07 14:21 鑫龍 閱讀(575) | 評論 (0) | 編輯 收藏
程序設計類
《C++ PRIMER》
《STL源碼解析》
系統編程類
《UNIX環境高級編程》W.Richard Stevens:非常經典的書。雖然初學者就可以看,但是事實上它是《Unix Network Programing》的一本輔助資料。國內的翻譯的《UNIX環境高級編程》的水平不怎么樣,現在有影印版,直接讀英文比讀中文來得容易。
《Unix網絡編程卷二》Unix網絡編程卷第二卷沒有涉及網絡的東西,主要講進程間通訊和Posix線程。
網絡編程類
《Unix網絡編程》第一卷講BSD Socket網絡編程接口和另外一種網絡編程接口的,不過現在一般都用BSD Socket,所以這本書只要看大約一半多就可以了。
《TCP/IP詳解》一共三卷,卷一講協議,卷二講實現,卷三講編程應用,也很經典的。
《用TCP/IP進行網際互連》一共三卷,內容講解十分精彩。卷一講原理,卷二講實現,卷三講高級協議。感覺上這一套要比Stevens的那一套要好,就連Stevens也不得不承認它的第一卷非常經典。事實上,第一卷即使你沒有一點網絡的知識,看完以后也會對網絡的來龍去脈了如指掌。第一卷中還有很多習題也設計得經典和實用,因為作者本身就是一位教師,并且卷一是國外研究生的教材。習題并沒有答案,留給讀者思考,因為問題得答案可以讓你成為一個中級的Hacker,這些問題的答案可以象Douglus索取,不過只有他只給教師卷二我沒有怎么看,卷三可以作為參考手冊,其中地例子也很經典。
《Linux 多線程服務端編程:使用 muduo C++ 網絡庫》
Linux系統管理類
《linux系統管理手冊》
《LINUX與UNIX SHELL編程指南》
《Advanced Bash Scripting Guide》
系統內核類
《Linux內核代碼情景分析》
《深入Linux內核源碼》
面向對象設計類
《設計模式》
《敏捷軟件開發:原則、模式與實踐》
《敏捷項目管理》
內功修煉類
《操作系統:設計與實現(第二版)》
《操作系統概念》
《數據結構與算法-面向對象的C++設計模式》
《編譯原理》國防陳火旺
《離散數學及其應用》
《計算機組織與體系結構?性能分析》
《深入理解計算機系統》【美】Randal E. Bryant David O'Hallaron著v
posted @ 2014-07-25 20:29 鑫龍 閱讀(1681) | 評論 (0) | 編輯 收藏
程序如下:
import java.util.*;
import java.io.*;
public class BadExecJavac
{
public static void main(String args[])
{
try
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
int exitVal = proc.exitValue();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
運行結果如下
E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavacjava.lang.IllegalThreadStateException: process has not exited
at java.lang.Win32Process.exitValue(Native Method)
at BadExecJavac.main(BadExecJavac.java:13)
這是因為當進程還沒有結束的情況下,調用exitValue方法會拋出IllegalThreadStateException.當然了我們會問為什嗎這個方法不會等到進程結束在返回一個合理的值?
在檢查Process類的所有可用方法以后我們發現WairFor()是一個更合適的方法。事實上waitFor也會返回exit value。這意味著你不可以同時用exitvalue和waitfor,而是只能選擇一個。
當然了也有情況你要在waitfor之前用exitvalue方法:就是你不想因為外部程序永遠無法完成而一直等待下去。
因此為了避免這個陷阱,我們要么捕獲IllegalThreadStateException異常,要么等待進程完成。我們相當然的以為可以用waitfor來等待程序的結束。代碼如下:
import java.util.*;import java.io.*;
public class BadExecJavac2{
public static void main(String args[]) {
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
這次在linux下面返回的結果是2,而在windows下面據說程序會掛起,關于其原因我們可以在jdk文檔中找到部分解釋:因為一些操作系統為標準的輸入輸出僅僅提供有限的緩沖區,當不能正確的將信息寫進輸入流或者從輸出流中獲取信息時,就會導致子進程的阻塞,甚至死鎖。現在我們就根據jdk文檔來處理javac進程的輸出,當你不帶任何參數運行javac時,它會打印出一系列的有用的提示信息。而這些會被傳送到stderr流中。我們可以寫程序在其返回前獲取這些信息。下面的代碼提供了一個平庸的解決方案。
import java.util.*;import java.io.*;public class MediocreExecJavac{
public static void main(String args[]) {
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
這次程序可以正確的輸出了提示信息,但是我們應該注意到其返回代碼是2,我們知道任何非0的返回代碼都表示程序不正常。所以我們需要進一步的查找原因。對于win32而言是file not found,很明顯javac期望我們提供編譯的文件。所以對于永遠掛起的問題,如果你運行的程序會有輸出或者要求輸出入時,你需要處理輸出和輸入。我在linux下面運行的結果是正確的。前面說了在win32下面2代表是文件沒有找到,而在這種情況下表明是dir.exe沒有找到,(因為根本就沒有這個文件,他們都被封裝到common.com (win95)或者cmd.exe中了。
下面我們列出一個正確的處理Process的輸入輸出流的方法。需要用一個線程類。
import java.util.*;
import java.io.*;
class StreamGobbler extends Thread{
InputStream is;
String type;
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
System.out.println(type + ">" + line);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
用于專門的處理輸入輸出。
public class GoodWindowsExec{
public static void main(String args[]) {
if (args.length < 1) {
System.out.println("USAGE: java GoodWindowsExec <cmd>");
System.exit(1);
}
try {
String osName = System.getProperty("os.name" );
String[] cmd = new String[3];
if( osName.equals( "Windows NT" ) ) {
cmd[0] = "cmd.exe" ;
cmd[1] = "/C" ;
cmd[2] = args[0];
} else if( osName.equals( "Windows 95" ) ) {
cmd[0] = "command.com" ;
cmd[1] = "/C" ;
cmd[2] = args[0]; }
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);
Process proc = rt.exec(cmd); // any error message?
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR"); // any output?
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT"); // kick them off
errorGobbler.start();
outputGobbler.start(); // any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
如果運行如下命令上面的代碼會調用word程序
>java GoodWindowExec “abc.doc”
也就是說文件類型如果window能夠識別它就會調用對應的程序處理。
StreamGlobbler的最重要作用是他會清空所有的傳遞給他的inputstream,這樣不會造成Process阻塞或者死鎖。
posted @ 2014-04-08 15:20 鑫龍 閱讀(3471) | 評論 (0) | 編輯 收藏


