せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】スレッド。全てのスレッド(非main)の終了を待つ、ファイルの並列読み出し

 

全てのスレッド(非main,current)の終了を待つ

def join_all
  main = Thread.main
  current = Thread.current
  all = Thread.list
  p all
  all.each{|t| t.join unless t==current || t==main}
end
Thread.new do
  sleep(1)
  Thread.new do
    sleep(1)
    p "thred thread"
  end
  join_all
  p "thread"
end
join_all
p "main"

■実行結果

[#<Thread:0x49d148 run>, #<Thread:0x307b900 run>]
[#<Thread:0x49d148 sleep>, #<Thread:0x307b900 run>, #<Thread:0x307b420 run>]
"thred thread"
"thread"
"main"

 

mainメソッドが終了すると実行が停止してしまう

n = 1
while n<=3
  Thread.new{p n}
  n += 1
end

■実行結果

(からっぽ)

なぜかというと、while内でThreadが作成される前にmainメソッドが終了したから。

mainメソッドを終わらせないと、予想通りの結果になる。
 

n = 1
while n<=3
  Thread.new{p n}
  n += 1
end
loop do
end

■実行結果

4
4
4

上記の通り、スレッド間で変数へのアクセスが共有できるため、
変数nはスレッドセーフではない。

スレッドセーフにしたいときは、単にブロックを使えば解決できる。

n = 1
while n<=3
  Thread.new(n){|x|p x}
  n += 1
end
loop do
end

■実行結果

1
2
3

 
もしくはイテレーターを使うと、外側のスレッド内でプライベートなので
ブロック実行中に値が変わることはない。

3.times do |i|
  Thread.new{sleep(1); p i}
end
loop do
end

■実行結果

1
0
2
0
2
1

スレッドによる並列処理なので、順序性は保障されない。
 

 

ファイルの並列読み出し

def conread(filenames)
  h={}
  filenames.each do |filename|
    h[filename] = Thread.new do
      File.open(filename){|f| f.read}
    end
  end
  h.each do |filename, thread|
    begin
      h[filename] = thread.value
    rescue
      h[filename] = $! #<= 最後に起きた例外のオブジェクト
    end
  end
end
dir = File.dirname(__FILE__) + File::SEPARATOR
filenames=[dir+"data1.txt", dir+"data2.txt", dir+"data3.txt"]
p conread(filenames)

■実行結果

{"C:/Users/HOGE_ADMIN/Desktop/net/data1.txt"=>"hogedata1", "C:/Users/HOGE_ADMIN/Desktop/net/data2.txt"=>"hogedata2", "C:/Users/HOGE_ADMIN/Desktop/net/data3.txt"=>"hogedata3"}