Ruby 多線程
每個正在系統上運行的程序都是一個進程。每個進程包含一到多個線程。
線程是程序中一個單一的順序控制流程,在單個程序中同時運行多個線程完成不同的工作,稱為多線程。
Ruby 中我們可以通過 Thread 類來創建多線程,Ruby的線程是一個輕量級的,可以以高效的方式來實現並行的代碼。
創建 Ruby 線程
要啟動一個新的線程,只需要調用 Thread.new 即可:
# 線程 #1 代碼部分 Thread.new { # 線程 #2 執行代碼 } # 線程 #1 執行代碼
實例
以下實例展示了如何在Ruby程序中使用多線程:
#!/usr/bin/ruby def func1 i=0
while i<=2 puts "func1 at: #{Time.now}" sleep(2) i=i+1 end end def func2 j=0
while j<=2 puts "func2 at: #{Time.now}" sleep(1) j=j+1 end end puts "Started At #{Time.now}" t1=Thread.new{func1()} t2=Thread.new{func2()} t1.join t2.join puts "End at #{Time.now}"
以上代碼執行結果為:
Started At Wed May 14 08:21:54 -0700 2014 func1 at: Wed May 14 08:21:54 -0700 2014 func2 at: Wed May 14 08:21:54 -0700 2014 func2 at: Wed May 14 08:21:55 -0700 2014 func1 at: Wed May 14 08:21:56 -0700 2014 func2 at: Wed May 14 08:21:56 -0700 2014 func1 at: Wed May 14 08:21:58 -0700 2014 End at Wed May 14 08:22:00 -0700 2014
線程生命周期
1、線程的創建可以使用Thread.new,同樣可以以同樣的語法使用Thread.start 或者Thread.fork這三個方法來創建線程。
2、創建線程后無需啟動,線程會自動執行。
3、Thread 類定義了一些方法來操控線程。線程執行Thread.new中的代碼塊。
4、線程代碼塊中最后一個語句是線程的值,可以通過線程的方法來調用,如果線程執行完畢,則返回線程值,否則不返回值直到線程執行完畢。
5、Thread.current 方法返回表示當前線程的對象。 Thread.main 方法返回主線程。
6、通過 Thread.Join 方法來執行線程,這個方法會掛起主線程,直到當前線程執行完畢。
線程狀態
線程有5種狀態:
線程狀態 | 返回值 |
---|---|
Runnable | run |
Sleeping | Sleeping |
Aborting | aborting |
Terminated normally | false |
Terminated with exception | nil |
線程和異常
當某線程發生異常,且沒有被rescue捕捉到時,該線程通常會被無警告地終止。但是,若有其它線程因為Thread#join的關系一直等待該線程的話,則等待的線程同樣會被引發相同的異常。
begin t = Thread.new do Thread.pass # 主線程確實在等join raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
使用下列3個方法,就可以讓解釋器在某個線程因異常而終止時中斷運行。
- 啟動腳本時指定-d選項,並以調試模時運行。
- 用
Thread.abort_on_exception
設置標志。 - 使用
Thread#abort_on_exception
對指定的線程設定標志。
當使用上述3種方法之一后,整個解釋器就會被中斷。
t = Thread.new { ... } t.abort_on_exception = true
線程同步控制
在Ruby中,提供三種實現同步的方式,分別是:
1. 通過Mutex類實現線程同步
2. 監管數據交接的Queue類實現線程同步
3. 使用ConditionVariable實現同步控制
通過Mutex類實現線程同步
通過Mutex類實現線程同步控制,如果在多個線程鍾同時需要一個程序變量,可以將這個變量部分使用lock鎖定。 代碼如下:
#encoding:gbk require "thread" puts "Synchronize Thread" @num=200 @mutex=Mutex.new def buyTicket(num) @mutex.lock
if @num>=num @num=@num-num puts "you have successfully bought #{num} tickets"
else puts "sorry,no enough tickets" end @mutex.unlock end ticket1=Thread.new 10 do
10.times do |value| ticketNum=15 buyTicket(ticketNum) sleep 0.01 end end ticket2=Thread.new 10 do
10.times do |value| ticketNum=20 buyTicket(ticketNum) sleep 0.01 end end sleep 1 ticket1.join ticket2.join
輸出結果如下:
Synchronize Thread you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets
除了使用lock鎖定變量,還可以使用try_lock鎖定變量,還可以使用Mutex.synchronize同步對某一個變量的訪問。
監管數據交接的Queue類實現線程同步
Queue類就是表示一個支持線程的隊列,能夠同步對隊列末尾進行訪問。不同的線程可以使用統一個對類,但是不用擔心這個隊列中的數據是否能夠同步,另外使用SizedQueue類能夠限制隊列的長度
SizedQueue類能夠非常便捷的幫助我們開發線程同步的應用程序,應為只要加入到這個隊列中,就不用關心線程的同步問題。
經典的生產者消費者問題:
#encoding:gbk require "thread" puts "SizedQuee Test" queue = Queue.new producer = Thread.new do
10.times do |i| sleep rand(i) # 讓線程睡眠一段時間 queue << i puts "#{i} produced" end end consumer = Thread.new do
10.times do |i| value = queue.pop sleep rand(i/2) puts "consumed #{value}" end end consumer.join 程序的輸出: SizedQuee Test 0 produced 1 produced consumed 0
2 produced consumed 1 consumed 2
3 produced consumed 34 produced consumed 4
5 produced consumed 5
6 produced consumed 6
7 produced consumed 7
8 produced 9 produced consumed 8 consumed 9
使用ConditionVariable實現同步控制
使用 ConditonVariable進行同步控制,能夠在一些致命的資源競爭部分掛起線程直到有可用的資源為止。
#encoding:gbk require "thread" puts "thread synchronize by ConditionVariable" mutex = Mutex.new resource = ConditionVariable.new a = Thread.new { mutex.synchronize { # 這個線程目前需要resource這個資源 resource.wait(mutex) puts "get resource" } } b = Thread.new { mutex.synchronize { #線程b完成對resourece資源的使用並釋放resource resource.signal } } a.join puts "complete"
mutex 是聲明的一個資源,然后通過ConditionVariable來控制申請和釋放這個資源。
b 線程完成了某些工作之后釋放資源resource.signal,這樣a線程就可以獲得一個mutex資源然后進行執行。 執行結果:
thread synchronize by ConditionVariable get resource complete
線程類方法
完整的 Thread(線程) 類方法如下:
序號 | 方法描述 |
---|---|
1 | Thread.abort_on_exception 若其值為真的話,一旦某線程因異常而終止時,整個解釋器就會被中斷。它的默認值是假,也就是說,在通常情況下,若某線程發生異常且該異常未被Thread#join等檢測到時,該線程會被無警告地終止。 |
2 | Thread.abort_on_exception= 如果設置為 true, 一旦某線程因異常而終止時,整個解釋器就會被中斷。返回新的狀態 |
3 | Thread.critical 返回布爾值。 |
4 | Thread.critical= 當其值為true時,將不會進行線程切換。若當前線程掛起(stop)或有信號(signal)干預時,其值將自動變為false。 |
5 | Thread.current 返回當前運行中的線程(當前線程)。 |
6 | Thread.exit 終止當前線程的運行。返回當前線程。若當前線程是唯一的一個線程時,將使用exit(0)來終止它的運行。 |
7 | Thread.fork { block } 與 Thread.new 一樣生成線程。 |
8 | Thread.kill( aThread ) 終止線程的運行. |
9 | Thread.list 返回處於運行狀態或掛起狀態的活線程的數組。 |
10 | Thread.main 返回主線程。 |
11 | Thread.new( [ arg ]* ) {| args | block } 生成線程,並開始執行。數會被原封不動地傳遞給塊. 這就可以在啟動線程的同時,將值傳遞給該線程所固有的局部變量。 |
12 | Thread.pass 將運行權交給其他線程. 它不會改變運行中的線程的狀態,而是將控制權交給其他可運行的線程(顯式的線程調度)。 |
13 | Thread.start( [ args ]* ) {| args | block } 生成線程,並開始執行。數會被原封不動地傳遞給塊. 這就可以在啟動線程的同時,將值傳遞給該線程所固有的局部變量。 |
14 | Thread.stop 將當前線程掛起,直到其他線程使用run方法再次喚醒該線程。 |
線程實例化方法
以下實例調用了線程實例化方法 join:
#!/usr/bin/ruby thr = Thread.new do # 實例化 puts "In second thread" raise "Raise exception" end thr.join # 調用實例化方法 join
以下是完整實例化方法列表:
序號 | 方法描述 |
---|---|
1 | thr[ name ] 取出線程內與name相對應的固有數據。 name可以是字符串或符號。 若沒有與name相對應的數據時, 返回nil。 |
2 | thr[ name ] = 設置線程內name相對應的固有數據的值, name可以是字符串或符號。 若設為nil時, 將刪除該線程內對應數據。 |
3 | thr.abort_on_exception 返回布爾值。 |
4 | thr.abort_on_exception= 若其值為true的話,一旦某線程因異常而終止時,整個解釋器就會被中斷。 |
5 | thr.alive? 若線程是"活"的,就返回true。 |
6 | thr.exit 終止線程的運行。返回self。 |
7 | thr.join 掛起當前線程,直到self線程終止運行為止. 若self因異常而終止時, 將會當前線程引發同樣的異常。 |
8 | thr.key? 若與name相對應的線程固有數據已經被定義的話,就返回true |
9 | thr.kill 類似於 Thread.exit 。 |
10 | thr.priority 返回線程的優先度. 優先度的默認值為0. 該值越大則優先度越高. |
11 | thr.priority= 設定線程的優先度. 也可以將其設定為負數. |
12 | thr.raise( anException ) 在該線程內強行引發異常. |
13 | thr.run 重新啟動被掛起(stop)的線程. 與wakeup不同的是,它將立即進行線程的切換. 若對死進程使用該方法時, 將引發ThreadError異常. |
14 | thr.safe_level 返回self 的安全等級. 當前線程的safe_level與$SAFE相同. |
15 | thr.status 使用字符串"run"、"sleep"或"aborting" 來表示活線程的狀態. 若某線程是正常終止的話,就返回false. 若因異常而終止的話,就返回nil。 |
16 | thr.stop? 若線程處於終止狀態(dead)或被掛起(stop)時,返回true. |
17 | thr.value 一直等到self線程終止運行(等同於join)后,返回該線程的塊的返回值. 若在線程的運行過程中發生了異常, 就會再次引發該異常. |
18 | thr.wakeup 把被掛起(stop)的線程的狀態改為可執行狀態(run), 若對死線程執行該方法時,將會引發ThreadError異常 |
作為新接觸的Ruby新手,不足之處,還請各位大神指點...
本文基於
署名 2.5 中國大陸許可協議發布,歡迎轉載,演繹或用於商業目的,但是必須且在文章頁面明顯位置給出原文鏈接
Dana、Li(包含鏈接),具體操作方式可參考此處。如您有任何疑問或者授權方面的協商,請留言或加Q群!
|