Threadクラスでスレッド生成、およびMutexクラスでスレッドセーフな排他的アクセス

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

Rubyでのマルチスレッド操作を少し勉強しました。
Threadクラスでただ単にスレッドを生成して実行した場合、共有リソースへの並列アクセスで、共有リソースのデータ不整合など問題が起こる場合がある。
Mutexを用いると排他的アクセスを実現し、データの整合性を保つことができます。
以下、実験してみたコード。

スポンサーリンク

Thread単純実行、Threadを使わずループ、Mutexの3パターン

# スレッドのリストのうち、最初の3つを確認
def thread_list(threads)
  num = 0
  threads.each do |t|
    p t
    num += 1
    break if num == 3
  end
end
 
# スレッドでの並列処理の場合、共有リソースへのアクセス時に注意する
# Mutexを使うと排他処理になり、共有リソースへ安全にアクセスできる。
ThreadNum = 10000
 
puts "スレッドセーフでなく、共有リソースを破壊する例"
class Count
  @@count = 0
  def count_up
    @@count += 1
  end
  def self.count
    return @@count
  end
  def self.count=(val)
    @@count = val
  end
end
 
puts Count.count
threads = []
# スレッドをThreadNum個作って、配列に追加
for i in 1..ThreadNum
  t = Thread.new do
    Count.new.count_up
  end
  threads << t
end
 
# ThreadNum個のスレッド実行が終了するのを待つ。
for i in 1..ThreadNum
  threads[i - 1].join
end
# スレッドを確認
thread_list(threads)
puts Count.count  # ThreadNum個になるはずだが…足りない
 
puts "--- スレッドを作らないで、単にループでカウントアップ ---"
Count.count = 0 # countをリセット
puts Count.count
for i in 1..ThreadNum
  Count.new.count_up
end
puts Count.count  # ThreadNum個になっている
 
puts "--- Mutexを用いて、排他的アクセス ---"
require 'thread'
class CountByMutex
  @@count = 0
  @@mutex = Mutex.new
  def count_up
    # mutexオブジェクトで排他的アクセスにする
    @@mutex.synchronize do
      @@count += 1
    end
  end
  def self.count
    return @@count
  end
end
 
puts CountByMutex.count
threads = []
# スレッドをThreadNum個作って、配列に追加
for i in 1..ThreadNum
  t = Thread.new do
    CountByMutex.new.count_up
  end
  threads << t
end
 
# ThreadNum個のスレッド実行が終了するのを待つ。
for i in 1..ThreadNum
  threads[i - 1].join
end
# スレッドを確認
thread_list(threads)
puts CountByMutex.count  # ThreadNum個になる

実行結果

スレッドセーフでなく、共有リソースを破壊する例
0
#<Thread:0x283e5a8 dead>
#<Thread:0x283e558 dead>
#<Thread:0x283e4f4 dead>
9940
--- スレッドを作らないで、単にループでカウントアップ ---
0
10000
--- Mutexを用いて、排他的アクセス ---
0
#<Thread:0x2afc4ac dead>
#<Thread:0x2afc420 dead>
#<Thread:0x2afc394 dead>
10000

排他制御の書き方は、こんな感じで良いのだろうか・・・(あんま自信ない)
Rubyの排他制御なメソッドの記述は、Javaのsynchronizedメソッドの書き方に少し似ています。

参考にしたページ:
逆引きRuby – スレッド
Thread – Rubyリファレンスマニュアル
第19章 スレッド(このページはムズイ・・・)

スポンサーリンク
 
スポンサーリンク