?
このドキュメントでは、 php中國(guó)語(yǔ)ネットマニュアル リリース
Ruby給了你兩個(gè)基本的方法來(lái)組織你的程序,使它同時(shí)能運(yùn)行自己的不同部分。你可以使用線程在程序內(nèi)部將任務(wù)分割,或者將任務(wù)分解為不同的程序,使用多進(jìn)程來(lái)運(yùn)行。下面我們輪流看一下這兩種方法。
一般來(lái)說(shuō)在Ruby中同時(shí)做兩件事情最簡(jiǎn)單的是使用Ruby線程。線程在進(jìn)程中,由Ruby解釋器實(shí)現(xiàn)。這使得Ruby線程也能完全的可移至,因?yàn)樗恍枰蕾?lài)特定的操作系統(tǒng),但是這樣你也不能利用本地線程(native threads)的優(yōu)點(diǎn)了。你也許有過(guò)線程饑餓得經(jīng)驗(yàn)(優(yōu)先級(jí)低的線程沒(méi)有機(jī)會(huì)運(yùn)行)。也許你會(huì)遇到線程死鎖,整個(gè)進(jìn)程都被掛起?;蛘咭恍┚€程的某些操作占用了CPU的太多時(shí)間,以至于其它線程不得不等待。但是,不要被這些潛在的問(wèn)題嚇倒,Ruby線程是使你程序并行運(yùn)行的輕量而有效的方法。
創(chuàng)建一個(gè)新的線程十分簡(jiǎn)單,下面的部分代碼并行的下載一些網(wǎng)頁(yè),每次有請(qǐng)求調(diào)用,這段代碼都將產(chǎn)生一個(gè)獨(dú)立的線程處理HTTP傳輸。
require?'net/http' pages?=?%w(?www.rubycentral.com ????????????www.awl.com ????????????www.pragmaticprogrammer.com ???????????) threads?=?[] for?page?in?pages ??threads?<<?Thread.new(page)?{?|myPage| ????h?=?Net::HTTP.new(myPage,?80) ????puts?"Fetching:?#{myPage}" ????resp,?data?=?h.get('/',?nil?) ????puts?"Got?#{myPage}:??#{resp.message}" ??} end threads.each?{?|aThread|??aThread.join?} |
Fetching:?www.rubycentral.com Fetching:?www.awl.com Fetching:?www.pragmaticprogrammer.com Got?www.rubycentral.com:??OK Got?www.pragmaticprogrammer.com:??OK Got?www.awl.com:??OK |
讓我們更詳細(xì)的看看這段代碼,這里有一些新技巧在里面。
新線程用 Thread.new
創(chuàng)建,這個(gè)方法接收一個(gè)block作為線程中要運(yùn)行的代碼,在我們的例子里面,這個(gè)block使用net/http庫(kù)從每個(gè)指定的站點(diǎn)抓取首頁(yè),從我們打印出來(lái)的信息來(lái)看,這些抓取活動(dòng)是同時(shí)進(jìn)行的。
當(dāng)我們創(chuàng)建線程的時(shí)候,我們將這個(gè)HTML頁(yè)面作為參數(shù)傳入,這個(gè)參數(shù)然后作為myPage參數(shù)傳給了block。為什么我們這么做而不是直接在block里面用page這個(gè)變量那?
一個(gè)線程共享了所有在它啟動(dòng)之前已經(jīng)存在的所有全局變量,實(shí)例變量和局部變量。善意的人有時(shí)候會(huì)告訴你,共享有時(shí)候不一定是好事。在這個(gè)例子里面,3個(gè)線程將共享page變量,第一個(gè)線程啟動(dòng)之后,page被設(shè)為http://www.rubycentral.com,在這個(gè)時(shí)候,創(chuàng)建線程的循環(huán)還沒(méi)有結(jié)束,第二次,page被設(shè)為http://www.awl.com,如果第一個(gè)線程還沒(méi)有使用page變量運(yùn)行完畢,那么可能這個(gè)線程會(huì)使用page的新值。這個(gè)bug將很難被跟蹤發(fā)現(xiàn)。
但是,在線程塊中創(chuàng)建的局部變量的作用域只在創(chuàng)建它的線程里,而不能被其它線程共享。在我們的例子里面,變量myPage將在線程被創(chuàng)建時(shí)賦值,每個(gè)線程都有自己的myPage變量的拷貝。
另一個(gè)很微妙的地方是程序的最后一行,為什么我們調(diào)用所創(chuàng)每個(gè)建線程的join方法?
當(dāng)一個(gè)Ruby程序結(jié)束退出的時(shí)候,它會(huì)殺死所有的線程,而不管它們的狀態(tài)。但是,你可以通過(guò)線程的 Thread#join
方法,使得主程序在等待這個(gè)線程結(jié)束后再退出。調(diào)用它的線程將會(huì)被阻塞,直到給定的線程結(jié)束。通過(guò)調(diào)用3個(gè)線程的join方法,你可以確定這三個(gè)請(qǐng)求將會(huì)在主程序退出之前完成。
?除了join,還有其它幾個(gè)用于控制線程的方便的方法。首先,你可以用 Thread.current
來(lái)訪問(wèn)當(dāng)前線程,你可以用 Thread.list
來(lái)取得所有線程列表,這個(gè)列表包括所有可運(yùn)行或者已停止的線程。為了檢測(cè)一個(gè)線程的狀態(tài),可以用方法 Thread#status
和 Thread#alive?
。
另外,你可以使用 Thread#priority=
來(lái)設(shè)置線程的不同的優(yōu)先級(jí)別。高優(yōu)先級(jí)的將會(huì)先于低優(yōu)先級(jí)的線程執(zhí)行。
在上面我們已經(jīng)說(shuō)過(guò),一個(gè)線程可以訪問(wèn)在它定義之前已經(jīng)存在的,在作用域范圍內(nèi)的變量,但是,在線程內(nèi)部定義的變量的作用域只在這個(gè)線程內(nèi)部有效。
但是,如果你想一個(gè)線程的變量能被其它線程訪問(wèn),包括主線程,該怎么辦呢?Ruby中的線程提供了一個(gè)功能,就是能夠按名字創(chuàng)建和訪問(wèn)線程內(nèi)的局部變量。你可以簡(jiǎn)單的把這個(gè)線程對(duì)象作為一個(gè)哈希,使用[ ]=方法寫(xiě)這個(gè)對(duì)象的變量值,用 [ ]來(lái)讀它的值。在這個(gè)例子里,?每個(gè)線程記錄當(dāng)前變量count的值,把它存到線程的局部變量,名字(key)為mycount。(這里有競(jìng)爭(zhēng)條件的出現(xiàn),但是我們還沒(méi)有談到同步問(wèn)題,這里我們只是忽略它們。)
count?=?0 arr?=?[] 10.times?do?|i| ??arr[i]?=?Thread.new?{ ????sleep(rand(0)/10.0) ????Thread.current["mycount"]?=?count ????count?+=?1 ??} end arr.each?{|t|?t.join;?print?t["mycount"],?",?"?} puts?"count?=?#{count}" |
8,?0,?3,?7,?2,?1,?6,?5,?4,?9,?count?=?10 |
主線程等待每個(gè)子線程結(jié)束之后,打印出來(lái)每個(gè)線程得到的count的值。我們?nèi)藶榈淖屆總€(gè)線程在取得count值之前休眠隨機(jī)的時(shí)間,這只是為了增加點(diǎn)趣味而已。
如果一個(gè)線程拋出一個(gè)沒(méi)有被處理的異常,將會(huì)怎樣呢?這依賴(lài)于系統(tǒng)設(shè)置Thread.abort_on_exception?,這個(gè)設(shè)置在第384頁(yè)和387頁(yè)。
如果abort_on_exception
被設(shè)置為false,這也是默認(rèn)的缺省值,那么如果一個(gè)線程出現(xiàn)錯(cuò)誤而沒(méi)有處理,則這個(gè)線程將會(huì)被殺死,其它沒(méi)有遇到異常的線程將繼續(xù)運(yùn)行。在下面的例子里,編號(hào)為3的線程將產(chǎn)生一個(gè)異常,而不會(huì)輸出任何東西,但是,你仍然可以看到其它線程的輸出。
threads?=?[] 6.times?{?|i| ??threads?<<?Thread.new(i)?{ ????raise?"Boom!"?if?i?==?3 ????puts?i ??} } threads.each?{|t|?t.join?} |
01 2 45prog.rb:4:?Boom!?(RuntimeError) from?prog.rb:8:in?`join' from?prog.rb:8 from?prog.rb:8:in?`each' from?prog.rb:8 |
但是,如果將abort_on_exception
設(shè)為true,一個(gè)線程出現(xiàn)沒(méi)有捕獲(處理)的異常,則所有的線程將都被殺死,上面的例子,如果編號(hào)為3的線程出錯(cuò),所有后面的線程都被殺死,不會(huì)產(chǎn)生任何輸出。
Thread.abort_on_exception?=?true threads?=?[] 6.times?{?|i| ??threads?<<?Thread.new(i)?{ ????raise?"Boom!"?if?i?==?3 ????puts?i ??} } threads.each?{|t|?t.join?} |
01 2 prog.rb:5:?Boom!?(RuntimeError) from?prog.rb:7:in?`initialize' from?prog.rb:7:in?`new' from?prog.rb:7 from?prog.rb:3:in?`times' from?prog.rb:3 |
在一個(gè)設(shè)計(jì)良好的應(yīng)用程序中,你應(yīng)該讓線程只做自己改作的事情;在一個(gè)多線程環(huán)境中創(chuàng)建一個(gè)基于時(shí)間的系統(tǒng)一般來(lái)說(shuō)不是一個(gè)好主意。
但是,有時(shí)候我們需要控制線程的運(yùn)行。也許我們的自動(dòng)點(diǎn)唱機(jī)有一個(gè)線程用來(lái)控制指示燈,我們希望在音樂(lè)停止播放的時(shí)候也停止指示燈。你也許在一個(gè)經(jīng)典的生產(chǎn)者-消費(fèi)者關(guān)系中有兩個(gè)線程,一個(gè)消費(fèi)者在生產(chǎn)者掛起的時(shí)候也必須掛起。
類(lèi)Thread
提供了很多方法用來(lái)控制線程調(diào)度,調(diào)用 Thread.stop
能停止當(dāng)前線程,而
Thread#run
將使某個(gè)線程啟動(dòng)運(yùn)行,調(diào)用
Thread.pass
將告訴線程調(diào)度器去執(zhí)行另外一個(gè)線程。?
Thread#join
和
Thread#value
將使調(diào)用者掛起,直到這個(gè)線程結(jié)束。
我們可以用下面代碼來(lái)示范一下上面的特點(diǎn)。
t?=?Thread.new?{?sleep?.1;?Thread.pass;?Thread.stop;?} | ||
t.status |
? |
"sleep" |
t.run | ||
t.status |
? |
"run" |
t.run | ||
t.status |
? |
false |
但是,使用這些原始的方法來(lái)控制線程調(diào)度實(shí)現(xiàn)同步,不管怎么說(shuō),都可能會(huì)遇到競(jìng)爭(zhēng)條件。如果你需要在線程中共享數(shù)據(jù),競(jìng)爭(zhēng)條件將會(huì)一直存在并且給調(diào)試帶來(lái)麻煩。幸運(yùn)的是,線程還有另一個(gè)工具:臨界區(qū)(critical section),使用它,我們能編寫(xiě)一個(gè)安全的同步方案。
用來(lái)阻塞一個(gè)線程運(yùn)行的低層的方法是使用全局的"線程臨界"(thread critical)條件。當(dāng)這個(gè)條件被設(shè)為true(用 Thread.critical=
方法)時(shí),調(diào)度器將不會(huì)讓任何已經(jīng)存在地線程執(zhí)行,但是,它不會(huì)阻止新線程的建立和運(yùn)行;一些特定的線程操作(比如停止或者殺死一個(gè)線程,在當(dāng)前線程中休眠,或者拋出一個(gè)異常)都會(huì)引起一個(gè)線程被調(diào)度,即使在臨界區(qū)之內(nèi)。
直接使用 Thread.critical=
雖然可行,但是它并不是很方便。幸運(yùn)的是,Ruby自帶了很多其它選項(xiàng),當(dāng)然,最好的兩個(gè)是thread庫(kù)模塊中的類(lèi)Mutex和類(lèi)ConditionVariable。關(guān)于它們的文檔從第457頁(yè)開(kāi)始。
Mutex
是一個(gè)為對(duì)互斥地訪問(wèn)某一共享對(duì)象而設(shè)計(jì)的一個(gè)簡(jiǎn)單的信號(hào)量鎖。也就是說(shuō),在一個(gè)時(shí)候,只有一個(gè)線程能持有這個(gè)鎖。其它線程可以繼續(xù)等待直到這個(gè)鎖可用,或者立即返回一個(gè)錯(cuò)誤不再繼續(xù)等待。
一個(gè)mutex經(jīng)常用于原子性的對(duì)一個(gè)共享對(duì)象進(jìn)行修改更新。假設(shè)我們需要更新一個(gè)事務(wù)中的兩個(gè)變量,比如下面的程序模擬增加兩個(gè)數(shù)的計(jì)數(shù)。這個(gè)更新假定是原子性的,外面的世界不可能看到這兩個(gè)數(shù)有不同的值。如果不使用互斥,則不能達(dá)到該目的。
count1?=?count2?=?0 | ||
difference?=?0 | ||
counter?=?Thread.new?do | ||
??loop?do | ||
????count1?+=?1 | ||
????count2?+=?1 | ||
??end | ||
end | ||
spy?=?Thread.new?do | ||
??loop?do | ||
????difference?+=?(count1?-?count2).abs | ||
??end | ||
end | ||
sleep?1 | ||
Thread.critical?=?1 | ||
count1 |
? |
184846 |
count2 |
? |
184846 |
difference |
? |
58126 |
這個(gè)例子顯示了在執(zhí)行的過(guò)程中count1和count2的值曾經(jīng)出現(xiàn)過(guò)不同,盡管最后還是一樣的。
幸運(yùn)的是,我們可以用互斥來(lái)改善這個(gè)例子。require?'thread' mutex?=?Mutex.new count1?=?count2?=?0 difference?=?0 counter?=?Thread.new?do ??loop?do ????mutex.synchronize?do ??????count1?+=?1 ??????count2?+=?1 ????end ??end end spy?=?Thread.new?do ??loop?do ????mutex.synchronize?do ??????difference?+=?(count1?-?count2).abs ????end ??end end |
sleep?1 | ||
mutex.lock | ||
count1 |
? |
21192 |
count2 |
? |
21192 |
difference |
? |
0 |
通過(guò)把需要訪問(wèn)共享數(shù)據(jù)的代碼放到muxtex的控制下,我們確保了數(shù)據(jù)的一致性。但不幸的是,你也從這些數(shù)字看到了,我們?cè)诮?jīng)受著性能上的損失。
有時(shí)候使用互斥(mutex )來(lái)保護(hù)對(duì)臨界數(shù)據(jù)的訪問(wèn)并不能滿(mǎn)足要求,比如假設(shè)我們?cè)谝粋€(gè)臨界區(qū)內(nèi),但是你還需要等待一個(gè)特殊的資源,如果你的線程這時(shí)候?yàn)榱说却@個(gè)資源而休眠,可能會(huì)導(dǎo)致其它線程不能釋放這個(gè)資源,因?yàn)樗鼈兌紵o(wú)法進(jìn)入這個(gè)臨界區(qū),原來(lái)的線程一直在鎖定著這個(gè)臨界區(qū)。你也許需要暫時(shí)的放棄對(duì)臨界區(qū)的控制,同時(shí)告訴其它線程你在等待某一資源。當(dāng)這個(gè)資源可用之后,你的線程同時(shí)需要重新得到對(duì)臨界區(qū)的控制權(quán)。
條件變量正是用在此處。一個(gè)條件變量是一個(gè)簡(jiǎn)單的信號(hào)量,它關(guān)聯(lián)一個(gè)特定的資源,在臨界區(qū)的保護(hù)范圍內(nèi)使用。當(dāng)你需要一個(gè)資源而這個(gè)資源暫時(shí)不可用的時(shí)候,你等待一個(gè)條件變量,這個(gè)操作將放棄對(duì)這個(gè)條件變量所在互斥(臨界區(qū)?)的鎖定。當(dāng)其它線程發(fā)送信號(hào)告訴這個(gè)變量可用之后,原來(lái)的線程停止等待立即取得對(duì)臨界區(qū)的鎖定。
require?'thread' mutex?=?Mutex.new cv?=?ConditionVariable.new a?=?Thread.new?{ ??mutex.synchronize?{ ????puts?"A:?I?have?critical?section,?but?will?wait?for?cv" ????cv.wait(mutex) ????puts?"A:?I?have?critical?section?again!?I?rule!" ??} } puts?"(Later,?back?at?the?ranch...)" b?=?Thread.new?{ ??mutex.synchronize?{ ????puts?"B:?Now?I?am?critical,?but?am?done?with?cv" ????cv.signal ????puts?"B:?I?am?still?critical,?finishing?up" ??} } a.join b.join |
A:?I?have?critical?section,?but?will?wait?for?cv(Later,?back?at?the?ranch...) B:?Now?I?am?critical,?but?am?done?with?cv B:?I?am?still?critical,?finishing?up A:?I?have?critical?section?again!?I?rule! |
另一個(gè)同步機(jī)制的實(shí)現(xiàn),可以參考Ruby發(fā)布程序中l(wèi)ib文件夾下的文件 monitor.rb
和sync.rb。
有時(shí)候你可能需要把一個(gè)任務(wù)分成幾個(gè)進(jìn)程級(jí)別的子任務(wù),或者你需要運(yùn)行一個(gè)另外的不使用Ruby寫(xiě)的程序,沒(méi)關(guān)系,Ruby有好幾種方法能使你創(chuàng)建和管理其它獨(dú)立的進(jìn)程。
在Ruby中產(chǎn)生一個(gè)新的進(jìn)程有好幾種方法,最簡(jiǎn)單的方法是運(yùn)行一個(gè)命令并且等到它結(jié)束。你也許運(yùn)行一些其它的獨(dú)立的命令,并且從主機(jī)得到返回的結(jié)果,Ruby提供了system方法和反引號(hào)方法。(反引號(hào)即"`")
system("tar?xzf?test.tgz") | ? | tar:?test.tgz:?Cannot?open:?No?such?file?or?directory\ntar: ?Error?is?not?recoverable:?exiting?now\ntar:?Child?returned?status?2\ntar: ?Error?exit?delayed?from?previous?errors\nfalse |
result?=?`date` | ||
result | ? | "Sun?Jun??9?00:08:50?CDT?2002\n" |
方法 Kernel::system
運(yùn)行一個(gè)指定的命令,如果這個(gè)命令存在且正確的運(yùn)行結(jié)束,這個(gè)方法返回true,否則返回false。如果這個(gè)命令運(yùn)行失敗,你可以從全局變量$?得到這個(gè)命令的返回代碼。
但system也有一個(gè)問(wèn)題,就是它所運(yùn)行程序的輸出簡(jiǎn)單的被指定到了你的程序的輸出,這可能不是你想要的。為了取得子進(jìn)程的標(biāo)準(zhǔn)輸出,你可以用反引號(hào),比如上面例子的 `date`
。注意,你需要用 String#chomp
來(lái)去除返回結(jié)果最后的換行符。
這中方法對(duì)簡(jiǎn)單的場(chǎng)合比較合適,我們只需要運(yùn)行一個(gè)命令,然后取得它的返回結(jié)果。但是,很多時(shí)候我們都需要對(duì)進(jìn)程有更多的控制,比如我們需要和子進(jìn)程進(jìn)行會(huì)話,向它輸入數(shù)據(jù),并且從它取回?cái)?shù)據(jù)。方法 IO.popen
正是具有這樣的作用。popen方法以一個(gè)子進(jìn)程來(lái)運(yùn)行一個(gè)命令,并且把這個(gè)進(jìn)程的標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出綁定到Ruby的IO對(duì)象。向IO對(duì)象寫(xiě)數(shù)據(jù),子進(jìn)程就可以從它的標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),而子進(jìn)程輸出的數(shù)據(jù),也可以通過(guò)Ruby的IO對(duì)象讀出來(lái)。
pig?=?IO.popen("pig",?"w+") pig.puts?"ice?cream?after?they?go?to?bed" pig.close_write puts?pig.gets |
iceway?eamcray?afterway?eythay?ogay?otay?edbay |
這個(gè)例子看起來(lái)很簡(jiǎn)單,打開(kāi)這個(gè)管道(pipe),寫(xiě)入一個(gè)短語(yǔ),然后讀取返回結(jié)果。但是pig程序并不會(huì)立即將它寫(xiě)的東西flush。假如上面例子中,如果pig.puts 后面緊跟pig.gets的話,程序?qū)⒈粧炱?,pig程序處理了我們的輸入,但是返回結(jié)果卻一直不會(huì)被寫(xiě)到管道。我們必須在這兩行之間插入 pig.close_write
,這將給pig的標(biāo)準(zhǔn)輸入發(fā)送一個(gè)文件結(jié)束標(biāo)志(end-of-file),然后我們需要的結(jié)果就會(huì)返回。
popen方法還有另外一些注意事項(xiàng)。如果指定的命令是一個(gè)減號(hào)("-"),Ruby將強(qiáng)迫產(chǎn)生一個(gè)新的Ruby解釋器,它將和原來(lái)的解釋器一起運(yùn)行。原來(lái)的解釋器進(jìn)程將得到一個(gè)IO對(duì)象作為返回結(jié)果,而子解釋器將得到nil。
pipe?=?IO.popen("-","w+") if?pipe ??pipe.puts?"Get?a?job!" ??$stderr.puts?"Child?says?'#{pipe.gets.chomp}'" else ??$stderr.puts?"Dad?says?'#{gets.chomp}'" ??puts?"OK" end |
Dad?says?'Get?a?job!' Child?says?'OK' |
除了popen方法,傳統(tǒng)的Unix調(diào)用 Kernel::fork
, IO.pipe
和 Kernel::exec
也可以在支持它們的系統(tǒng)上使用。許多IO方法和 Kernel::open
也能產(chǎn)生新的子進(jìn)程,使用方法是將文件名前面加上一個(gè)豎線``|
'' 。注意你不能用 File.new
來(lái)產(chǎn)生一個(gè)子進(jìn)程,這個(gè)方法只是用于文件。
有時(shí)候我們并不需要這樣處理:我們只想把產(chǎn)生的子進(jìn)程賦給一個(gè)變量,然后繼續(xù)處理自己的事務(wù)。一段時(shí)間以后,我們也許還需要一下這個(gè)進(jìn)程是否結(jié)束。比如,我們需要從主程序分離一個(gè)需要很長(zhǎng)運(yùn)行時(shí)間的外部排序:
exec("sort?testfile?>?output.txt")?if?fork?==?nil #?The?sort?is?now?running?in?a?child?process #?carry?on?processing?in?the?main?program #?then?wait?for?the?sort?to?finish Process.wait |
系統(tǒng)調(diào)用 Kernel::fork
在父進(jìn)程中返回fork產(chǎn)生的子進(jìn)程id,在子進(jìn)程中返回nil,所以,上面例子中子進(jìn)程將調(diào)用 Kernel::exec
來(lái)運(yùn)行一個(gè)外部的排序。一段時(shí)間以后,我們使用 Process::wait
,這將等待排序完成,然后返回這個(gè)進(jìn)程的id。(pid)
如果你需要在子進(jìn)程退出后通知父進(jìn)程(而不是等待子進(jìn)程結(jié)束),可以用 Kernel::trap
來(lái)對(duì)返回的信號(hào)進(jìn)行處理。比如這里我們建立了一個(gè)用于捕獲SIGCLD
信號(hào)的trap,這個(gè)信號(hào)的意思是“子進(jìn)程結(jié)束(死亡)”
trap("CLD")?{ ??pid?=?Process.wait ??puts?"Child?pid?#{pid}:?terminated" ??exit } exec("sort?testfile?>?output.txt")?if?fork?==?nil #?do?other?stuff... |
Child?pid?31842:?terminated |
IO.popen
也能像
File.open
那樣接受一個(gè)block。通過(guò)傳遞一個(gè)參數(shù)給 popen
作為一個(gè)命令,比如 date
,然后,這個(gè)block將得到一個(gè)IO對(duì)象作為參數(shù)。
IO.popen?("date")?{?|f|?puts?"Date?is?#{f.gets}"?} |
Date?is?Sun?Jun??9?00:08:50?CDT?2002 |
這個(gè)IO對(duì)象將會(huì)在BLOCK結(jié)束之后自動(dòng)關(guān)閉,就如同 File.open
一樣。
如果你給 Kernel::fork
提供一個(gè)block,那么這些block中的代碼將在Ruby的子進(jìn)程中運(yùn)行,而父進(jìn)程在block結(jié)束后繼續(xù)運(yùn)行。
fork?do ??puts?"In?child,?pid?=?#$$" ??exit?99 end pid?=?Process.wait puts?"Child?terminated,?pid?=?#{pid},?exit?code?=?#{$??>>?8}" |
In?child,?pid?=?31849 Child?terminated,?pid?=?31849,?exit?code?=?99 |
最后一個(gè)問(wèn)題,為什么我們子進(jìn)程的返回代碼 $?
要右移8位?這是Posix系統(tǒng)的特點(diǎn),退出代碼(exit code)的低8位是程序結(jié)束的原因,高8位才是真正的退出代碼。