LoginSignup
1

More than 1 year has passed since last update.

Rubyでスレッドセーフでないコードを実行するとどうなる?

Last updated at Posted at 2022-06-18

マルチスレッドでプログラムを実行するとメモリ空間は同じものを使う。では、Rubyにおいて複数スレッドを立てて同じリソースに同時にアクセスするとどうなるのか?

IOを伴わない場合

Rubyでスレッドの生成はThreadクラスを使う。
以下のコードを実行してみる。
1000個スレッドを立ててそれぞれのスレッドから変数countを書き換える。

count = 0
threads = []

# 1000スレッドが同時に変数countにアクセスするとどうなるか?
1000.times do
  threads << Thread.new do
    count +=1
  end
end

threads.each(&:join)
puts count

結果は、スレッドセーフではないコードなのに、何度実行しても正常にインクリメントされ結果は1000になる。

これは、Ruby(ここではMRIに限る)ではGiant VM lock (GVL)(別名 Global Interpreter Lock(GIL))が実装されているためいくつスレッドを立てようと複数のスレッドが同時に実行されることはないため。

IOを伴う場合

前の例ではGVLによる排他制御が効いたが、IOを伴う場合はこのロックが解放されるので注意が必要。

IO 関連のブロックする可能性があるシステムコールを行う場合には GVL を解放します。その場合にはスレッドは同時に実行され得ます。https://docs.ruby-lang.org/ja/latest/class/Thread.html

インクリメントの前に標準出力処理を入れて試してみる。

count = 0
threads = []
flag = true

1000.times do
  threads << Thread.new do
    if flag 
      puts 'hoge' # インクリメントの前に標準出力をはさむ
      count +=1
      flag = false
    end
  end
end

threads.each(&:join)
puts count

結果は1000だったり1だったり999だったり、毎回異なる。

IOを伴う処理で、スレッド間で共通のリソース(メモリ・ファイルなど)にアクセスする場合にはスレッド間の競合に注意する必要がある。
なお競合を回避するためにはMutexクラスが用意されている。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1