Open6
Ractor プログラミングメモ
標準入出力へのアクセス
Ruby では特殊変数 $stdin
$stdout
$stderr
および定数 Object::STDIN
Object::STDOUT
Object::STDERR
を通して標準入出力にアクセスできる。
Ractor 内で使えるのは前者だけ。後者は Ractor::IsolationError を発生させる。
r = Ractor.new do
# this is ok
$stdout.puts "Hello via $stdout"
# can not access non-shareable objects in constant Object::STDOUT by non-main Ractor. (Ractor::IsolationError)
STDOUT.puts "Hello via STDOUT"
Ractor.yield 1
end
r.take
Logger
標準の Logger
は shareable ではなく、Ractor の外側で作成したインスタンスを Ractor 内で使うことができない。
Ractorはshareableであるため、ロガーとして振る舞うRactorを作成し、それを他のRactorにわたすとよい。ロガーとしての使い勝手はもちろん Logger
より劣る。
logger = Ractor.new do
require 'logger'
l = Logger.new($stdout)
loop do
log = Ractor.receive
break if log == :close
l.info(log) # debug や warn などの呼び分けはできない
end
Ractor.yield 1
end
r1 = Ractor.new(logger) do |logger|
logger.send("Hello from r1")
Ractor.yield 1
end
r2 = Ractor.new(logger) do |logger|
logger.send("Hello from r2")
Ractor.yield 1
end
# join r1 and r2
r1.take
r2.take
logger.send(:close)
logger.take
Ractor.new { } の範囲を小さく留める
勢いよくRack server全体をRactorにしまいたい気持ちは分かるが、Ractor-safe / Ractor-shareable でない要素はものすごく多い。
CPU boundな処理を見極めて、そこだけを別Ractorに動かすのでも十分に意味がある…… と思いたい。
rc = Ractor.new(str) { |str| Ractor.yield JSON.parse(str) }
parsed = rc.take
proc は Ractor から持ち出すこともできない
proc はかなり色々なところに潜んでいる。たとえば Hash の初期化用 proc を指定している場合、その Hash は yield できなくなる。
Ractor.new do
hash = Hash.new { |h, k| h[k] = [] }
# ...
Ractor.yield hash # <internal:ractor>:644:in 'Ractor.yield': allocator undefined for Proc (TypeError)
end
この例に限っては hash.default = nil
とすることで yield 可能になる。
Ractor 間で共有したい状態は Ractor にする
ミュータブルな変数は Ractor shareable ではないが、Ractor にくるむことで shareable にできる。
counter の例:
counter_ractor = Ractor.new do
count = 0
while msg = Ractor.receive
case msg
in [:incr, n]
count += n
in [:current, receiver]
receiver.send(count) # 受信した Ractor のチャンネルを通して結果を送り返す
end
end
end
r1 = Ractor.new(counter_ractor) do |counter|
loop do
counter.send([:incr, 1])
end
end
r2 = Ractor.new(counter_ractor) do |counter|
loop do
counter.send([:current, Ractor.current]) # 自身の Ractor のチャンネルを通して結果を受信する
p Ractor.receive
sleep 1
end
end
r1.take; r2.take # join
can not isolate a Proc because it accesses outer variables
外の変数にアクセスするとダメ
ary = [1, 2, 3]
Ractor.new do
p ary
end
↓
<internal:ractor>:282:in 'Ractor.new': can not isolate a Proc because it accesses outer variables (ary). (ArgumentError)
from err.rb:2:in '<main>'
外にある変数名を使っているとダメ
conn = server.accept
rc = Ractor.new do
conn = Ractor.receive # うっかり外にある変数名と同じものを使ってしまうとダメ
# ...
end
rc.send(conn, move: true)
作成者以外のコメントは許可されていません