Rubyの非同期処理:FiberとThreadを使いこなす

Rubyでは、非同期処理を実現するためにFiberとThreadが提供されています。これらを理解し活用することで、効率的でスケーラブルなプログラムを構築できます。本記事では、それぞれの特徴と実践的な使用方法を掘り下げます。
目次
非同期処理とは
非同期処理は、複数の処理を同時に進行させるプログラミング手法です。IO操作や重い計算処理を効率化するために使用されます。
Fiberの概要
Fiberは軽量なコルーチンで、任意のタイミングで制御を切り替えることができます。
fiber = Fiber.new do
puts "Fiber 開始"
Fiber.yield
puts "Fiber 再開"
end
puts "Fiber 呼び出し"
fiber.resume
puts "メイン処理"
fiber.resume
Threadの概要
ThreadはRubyにおける並行処理を実現するクラスです。スレッドは独立した実行単位として動作します。
thread = Thread.new do
puts "スレッドの処理開始"
sleep(1)
puts "スレッドの処理終了"
end
puts "メインスレッド"
thread.join
FiberとThreadの違い
Fiberは明示的に制御を切り替えるコルーチンで、Threadは並行処理をサポートする実行単位です。それぞれの適用シーンを比較します。
Fiberを活用した非同期処理
Fiberを使って非同期の操作をシミュレートします。
fib1 = Fiber.new do
puts "Fiber 1 - 処理1"
Fiber.yield
puts "Fiber 1 - 処理2"
end
fib2 = Fiber.new do
puts "Fiber 2 - 処理1"
Fiber.yield
puts "Fiber 2 - 処理2"
end
fib1.resume
fib2.resume
fib1.resume
fib2.resume
Threadを活用した並行処理
複数のスレッドで並行処理を実現します。
threads = []
5.times do |i|
threads << Thread.new do
puts "スレッド #{i} 開始"
sleep(1)
puts "スレッド #{i} 終了"
end
end
threads.each(&:join)
Threadの安全性と同期
スレッド間でのデータ競合を防ぐため、Mutexを使用します。
mutex = Mutex.new
counter = 0
threads = 10.times.map do
Thread.new do
mutex.synchronize do
temp = counter
sleep(0.1)
counter = temp + 1
end
end
end
threads.each(&:join)
puts "カウンターの最終値: #{counter}"
Fiberを利用したジェネレータの構築
Fiberを使ってデータの生成を行うジェネレータを構築します。
def generator
Fiber.new do
10.times do |i|
Fiber.yield i
end
end
end
gen = generator
while gen.alive?
puts gen.resume
end
ThreadとFiberの組み合わせ
ThreadとFiberを組み合わせて柔軟な非同期処理を実現します。
threads = []
3.times do
threads << Thread.new do
fiber = Fiber.new do
3.times do |i|
puts "Fiber: #{i}"
Fiber.yield
end
end
while fiber.alive?
fiber.resume
sleep(0.5)
end
end
end
threads.each(&:join)
FiberSchedulerによる非同期IO
Ruby 3.0で導入されたFiberSchedulerを使った非同期IOの例です。
require 'socket'
Fiber.set_scheduler(Fiber::Scheduler.new)
server = TCPServer.new(3000)
Fiber.schedule do
loop do
client = server.accept
Fiber.schedule do
client.puts "こんにちは!"
client.close
end
end
end
非同期処理のパフォーマンス比較
FiberとThreadのパフォーマンスを比較します。
require 'benchmark'
n = 10_000
Benchmark.bm do |x|
x.report("Fiber") do
fibers = n.times.map do
Fiber.new { Fiber.yield }
end
fibers.each(&:resume)
end
x.report("Thread") do
threads = n.times.map do
Thread.new { sleep(0) }
end
threads.each(&:join)
end
end
非同期処理の課題と限界
非同期処理の適用範囲や制限について考察します。
まとめ
RubyのFiberとThreadを使いこなすことで、非同期処理を効果的に実現できます。それぞれの特性を理解し、適切なシナリオで使用することで、パフォーマンスを最大化できます。
-
前の記事
Rubyの例外処理をマスターする:エラーを安全に扱うための設計 2024.12.16
-
次の記事
RHELのスナップショット管理 – BTRFSを使用したバックアップ手法 2024.12.16
コメントを書く