標籤:style java 使用 strong 檔案 資料
硬體多核時代的軟體業
以前計算能力的提升一直在摩爾定律的指引下,沿著提升CPU時鐘頻率這條道路前進,從初期的幾十MHz到如今的幾GHz。但是,進入2002年以 來,CPU提升主頻的困難越來越大,因為主頻的提升帶來了散熱和功耗的大幅增加等問題。幾年前,英特爾和AMD都調整了研究方向,開始研究在同一CPU中 放置多個執行核心。
儘管多核是一種硬體技術,但硬體和軟體是相互依存的,硬體只是一種物質基礎,只有有了軟體的支援,才能使硬體擁有用武之地。如今,多核的優勢已成共識:一在於“降低功耗”,解決了以往靠主頻提高帶來的後遺症問題;
二在於計算效能更強,能夠滿足使用者同時進行多任務處理和多任務計算環境的要求。要發揮多核的這些優勢,相應軟體的支援必不可少。其中,最主要的就是軟體程式要能夠平行處理。
並發和並行的關係
並發和並行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不同的任務。前者是邏輯上的同時發生(simultaneous),而後者是物理上的同時發生.並發性(concurrency),又稱共行性,是指能處理多個同時性活動的能力,並發事件之間不一定要同一時刻發生。並行(parallelism)是指同時發生的兩個並發事件,具有並發的含義,而並發則不一定並行。
來個比喻:並發和並行的區別就是一個人同時吃三個饅頭和三個人同時吃三個饅頭。
進程與線程
什麼是進程(Process),普通的解釋就是,進程是程式的一次執行,而什麼是線程(Thread),線程可以理解為進程中的執行的一段程式片段。在一個多任務環境中下面的概念可以協助我們理解兩者間的差別:
進程間是獨立的,這表現在記憶體空間,上下文環境;線程運行在進程空間內。一般來講(不使用特殊技術)進程是無法突破進程邊界存取其他進程內的儲存空間;而線程由於處於進程空間內,所以同一進程所產生的線程共用同一記憶體空間。
同一進程中的兩段代碼不能夠同時執行,除非引入線程。線程是屬於進程的,當進程退出時該進程所產生的線程都會被強制退出並清除。線程佔用的資源要少於進程所佔用的資源。進程和線程都可以有優先順序。線上程系統中進程也是一個線程。可以將進程理解為一個程式的第一個線程。
現代的作業系統幾乎都是多任務作業系統,我們通常使用的電腦中只有一個CPU,也就 是說只有一顆心,要讓它一心多用,同時運行多個進程,就必須使用並發技術。實現並發技術相當複雜,最容易理解的是“時間片輪轉進程調度演算法”,它的思想簡 單介紹如下:在作業系統的管理下,所有正在啟動並執行進程輪流使用CPU,每個進程允許佔用CPU的時間非常短(比如10毫秒),這樣使用者根本感覺不出來 CPU是在輪流為多個進程服務,就好象所有的進程都在不間斷地運行一樣。但實際上在任何一個時間內有且僅有一個進程佔有CPU。
如果一台電腦有多個CPU,情況就不同了,如果進程數小於CPU數,則不同的進程可以分配給不同的CPU來運行,這樣,多個進程就是真正同時啟動並執行,這便是並行。但如果進程數大於CPU數,則仍然需要使用並發技術。
如果在多CPU電腦中只運行一個進程(線程),就不能發揮多CPU的優勢。那麼在多CPU的硬體條件下,一個進程是不是會在多個CPU上運行呢?
其實這還是要明白 進程是系統進行資源分派和調度的單位 線程是作業系統分配處理器(CPU)時間的基本單元,是系統中最小的執行單元。一個進程被分配了獨立的資源空間,但裡面真正執行的還是一個一個線程,在運行一個進程的時候,它裡的若干線程會被空間的CPU調度執行。 所以一個進程會在多CPU上運行,也就是你在一個多CPU上只行動一個程式,也會看到多CPU在不停的切換
動態語言的現狀
在Python,Ruby中,如果我們願意,我們完全可以像大多數的c,c++,java開發人員一樣,隨時隨地的使用線程。但問題是,Ruby,Python使用了一個Global Interpreter Lock(aka GIL).這個GIL是一個保護資料完整性的鎖機制。GIL每次只允許資料被一個線程修改,因此不讓線程損壞資料,也不允許其真正的並發運行。這就是為什 麼有些人說,Ruby和Python並不具備真正的並發性。
多進程或者fork進程 ,這是使用Ruby和Python,PHP並發最常用的解決方案。因為預設的語言並沒有能力實現真正的並發,或者因為你想避免線程編程的挑戰,你可能想去開啟更多的進程。如果你並不想在進程間共用狀態,這是很容易的。[
大多數的程式設計語言都不容易實現並發,函數式語言簡化了並行開發,如Erlang,Scala,Lisp,Clojure.這得益於它既不用共用記憶體,也不會產生副作用(side effect)的函數。
PHP多進程方法
1.exec或system
2.popen會建立一個管道來串連該進程,然後使用fread/fgets/stream_get_contents來讀取該進程返回的結果。跟 exec或system之類的函數不同的是,exec會等待命令執行完成,再運行下面的代碼,但popen不會。proc_open又更加強大一些,支援 stdin和stdout,路徑設定等等。
3.PHP有一組進程式控制制函數(編譯時間需要 –enable-pcntl與posix擴充),使得php能在*nix系統中實現跟c一樣的建立子進程、使用exec函數執行程式、處理訊號等功能。 PCNTL使用ticks來作為訊號處理機制(signal handle callback mechanism),可以最小程度地降低處理非同步事件時的負載。何謂ticks?Tick 是一個在程式碼片段中解譯器每執行 N 條低級語句就會發生的事件,這個程式碼片段需要通過declare來指定。
PHP實現守護進程
編寫Daemon程式有一些基本的規則,以避免不必要的麻煩。
1、首先是程式運行後調用fork,並讓父進程退出。子進程獲得一個新的進程ID,但繼承了父進程的進程組ID。
2、調用setsid建立一個新的session,使自己成為新session和新進程組的leader,並使進程沒有控制終端(tty)。
3、改變當前工作目錄至根目錄,以免影響可負載檔案系統。或者也可以改變到某些特定的目錄。
4、設定檔案建立mask為0,避免建立檔案時許可權的影響。
5、關閉不需要的開啟檔案描述符。因為Daemon程式在後台執行,不需要於終端互動,通常就關閉STDIN、STDOUT和STDERR。其它根據實際情況處理。
另一個問題是Daemon程式不能和終端互動,也就無法使用printf方法輸出資訊了。我們可以使用syslog機制來實現資訊的輸出,方便程式的調試。在使用syslog前需要首先啟動syslogd程式,關於syslogd程式的使用請參考它的man page,或相關文檔,我們就不在這裡討論了。
處理序間通訊IPC
Linux下處理序間通訊的幾種主要手段簡介:
- 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間的通訊;
- 訊號(Signal):訊號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於處理序間通訊外,進程還可以發送訊號 給進程本身;linux除了支援Unix早期訊號語義函數sigal外,還支援語義符合Posix.1標準的訊號函數sigaction(實際上,該函數 是基於BSD的,BSD為了實現可靠訊號機制,又能夠統一對外介面,用sigaction函數重新實現了signal函數);
- 報文(Message)隊列(訊息佇列):訊息佇列是訊息的連結資料表,包括Posix訊息佇列system V訊息佇列。有足夠許可權的進程可以向隊列中添加訊息,被賦予讀許可權的進程則可以讀走隊列中的訊息。訊息佇列克服了訊號承載資訊量少,管道只能承載無格式字 節流以及緩衝區大小受限等缺點。
- 共用記憶體:使得多個進程可以訪問同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制運行效率較低而設計的。往往與其它通訊機制,如訊號量結合使用,來達到進程間的同步及互斥。
- 訊號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
- 套介面(Socket):更為一般的處理序間通訊機制,可用於不同機器之間的處理序間通訊。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支援通訊端。
注意事項
- PHP的記憶體管理機制
- 進程IO異常
- 資源fork問題
- 通訊的複雜度
- 進程的殭屍回收機制
具體的例子是
1、最好結合cronjob來定時跑指令碼,這樣即使你的代碼沒有管理好記憶體,也不要緊,跑完一次就釋放掉了。
2、對於必須常駐進程的指令碼,一定要在while (1) {} 這樣一個死迴圈裡面運行代碼。這樣只要代碼不出狀況,指令碼就不會停止。
3、echo 不能用,而是用log 代替。用寫日誌的方法代替echo。因為echo 是向螢幕輸出一個字元,如果沒有任何輸出的對象,就會報一個致命錯誤。
4、如果MYSQL,要每次重新串連MYSQL或者至少使用時判斷串連。因為你的指令碼運行期間難保mysql不會重啟,一旦重啟,之前串連資源就失效了,會報這樣一個錯誤:mysql has go away。
5、新產生的變數,沒用了要馬上釋放。
6、 如果要訪問檔案,首先要 clearstatcache, 否則很有可能會不精確的統計,如果你頻繁開啟檔案,檔案的handle 值會不斷增加,等到超過整數的最大值,程式就無法開啟檔案