Ruby 2.3.0 で Thread.exclusive
が deprecated になっていて、使っていると警告が出力されるようになった。
Thread.exclusive is deprecated, use Mutex
で、使っているライブラリでもバンバンこの警告が出まくっていたので、「よーしじゃあ修正しとこうかな」と思ったけど、よく考えたらそもそもこの Thread.exclusive
のことをよく知らなかった。
Thread.exclusive とは何だったのか
短いので、コードを見るのが一番早い。
prelude.rb
class Thread
MUTEX_FOR_THREAD_EXCLUSIVE = Mutex.new # :nodoc:
# call-seq:
# Thread.exclusive { block } => obj
#
# Wraps the block in a single, VM-global Mutex.synchronize, returning the
# value of the block. A thread executing inside the exclusive section will
# only block other threads which also use the Thread.exclusive mechanism.
def self.exclusive
warn "Thread.exclusive is deprecated, use Mutex", caller
MUTEX_FOR_THREAD_EXCLUSIVE.synchronize{
yield
}
end
end
ここからわかる通り、Thread.exclusive
というのは、Thread
クラスに MUTEX_FOR_THREAD_EXCLUSIVE
という名前で定義された Mutex
に対して synchronize
を呼び出してブロックを実行している。つまり、(少なくとも 1.9 以降では) Thread.exclusive
は Mutex
を使った排他制御に他ならず、各スレッドが Thread.exclusive
を呼び出しているクリティカルセクションにおいてのみスレッドセーフに実行することができる。言い変えると、かつての 1.8 の Thread.exclusive
のように、その実行中はスレッドのコンテキストスイッチを一切行わない、というものではない。
Thread.exclusive はなぜ deprecated になったのか
バグトラッカーの「Why was Thread.exclusive deprecated?」というチケットを見ると、
- グローバルに作用する
Thread.exclusive
は予期せぬ競合を引き起こす可能性があるため - 定義した
Mutex
の初期化をrequire
を利用して行えば、読み込みは1度に限られスレッドセーフであるため
などの点が挙げられている。実際の経緯は知らないけど、確かにおっしゃる通りですと感じる。
ただ、「Thread.exclusive
が暗黙的な Mutex
である」と知ってか知らずか、Thread.exclusive
は色んなところで使われているようなので、それを一般的な Mutex
に置き換える PR が捗りそうではある。これ、るりまを参照してる分にはハッキリと「他の排他制御の方法を検討してください」と書かれてるけど、ruby-doc.org のリファレンス では 1.9 以降もしばらくミスリードっぽい記載になっていたようなので、その影響もあって広く使われてしまったのだろうか。
で、修正は素直に Mutex
に置き換えればいいだけだろうけど、この修正のように、いくら 1 インスタンスしか生成されないクラスであったとしても、シングルトンではないインスタンス変数で Mutex
を定義するのはちょっと落ち着かない気がするなぁ。