今回のサンプルプログラムでは、メインとなるプログラムのスレッドのほかに、2つのスレッドを作成します。それぞれのスレッドで文字列を並行して出力させ、実行結果がどうなるか見てみます。
thread_sample1.rb
require 'thread' # 1つ目のスレッド(作成後、すぐに走り始める) Thread.new do 5.times do puts "Thread1"; $stdout.flush end end # 2つ目のスレッド(作成後、すぐに走り始める) Thread.new do 5.times do puts "Thread2"; $stdout.flush end end # 上記2つのスレッドの実行が終わるのを待つ Thread.list.each { |t| t.join unless t == Thread.current } puts "Main Thread"
実行結果
(※同じプログラムでも、実行環境によって実行結果が変わると思います。)
$ ruby thread_sample1.rb Thread1 Thread2Thread1 Thread2 Thread1 Thread2 Thread2Thread1 Thread1Thread2 Main Thread
プログラムではputsを使って文字列を1行単位で出力させている($stdout.flushも敢えて実行させている)はずですが、実際の実行結果では、なぜか"Thread2Thread1"と続けて文字列が出力されました。このあたりがスレッド処理特有の現象で、1つのメインプログラムだけのシングルスレッドではなかなか予想できないことが、起きるようになります。
この"Thread2Thread1"と出力された原因は、おそらく、Thread2が出力バッファからflushされる前に、Thread1が出力バッファに書きこまれたためです。よって、出力部分をMutexでロックしてflushまでをアトミックな処理にするようプログラムを変更してみます。
thread_sample2.rb
require 'thread' # Mutex lock = Mutex.new # 1つ目のスレッド(作成後、すぐに走り始める) Thread.new do 5.times do lock.synchronize { puts "Thread1"; $stdout.flush } end end # 2つ目のスレッド(作成後、すぐに走り始める) Thread.new do 5.times do lock.synchronize { puts "Thread2"; $stdout.flush } end end # 上記2つのスレッドの実行が終わるのを待つ Thread.list.each { |t| t.join unless t == Thread.current } puts "Main Thread"
実行結果
(※同じプログラムでも、実行環境によって実行結果が変わると思います。)
$ ruby thread_sample2.rb Thread1 Thread1 Thread1 Thread1 Thread1 Thread2 Thread2 Thread2 Thread2 Thread2 Main Thread
今度は、putsからflushまでがアトミックな処理として行われたことを反映して、Thread1, Thread2の文字列出力が1行ごとに出力されています。スレッドを使うと予期せぬ現象が起こり、バグになりやすいため扱いが難しいところもありますが、その分使いこなせるようになるとおもしろいと思います。
参考ページ
ミューテックス - Wikipedia -
アトミック性 - Wikipedia -
スレッドセーフ - Wikipedia -
0 件のコメント:
コメントを投稿