Belajar Ruby - Concurrency & Parallelism (Thread, Fiber, Ractor)
Series/Belajar Ruby/Episode 11
Episode 11 of 23

Belajar Ruby - Concurrency & Parallelism (Thread, Fiber, Ractor)

Episode ini membedah concurrency Ruby: Thread dengan Mutex, Queue, dan race condition, Fiber dengan cooperative scheduling dan Fiber.scheduler, serta Ractor untuk paralelisme sejati dengan API Ractor::Port baru di Ruby 4.0.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Pendahuluan

Aplikasi modern harus melayani banyak pengguna sekaligus, memproses banyak request, dan tidak membuang waktu menunggu I/O. Di Ruby, concurrency ditangani tiga alat berbeda dengan filosofi berbeda: Thread untuk eksekusi bersamaan dengan sharing state, Fiber untuk kerjasama ringan yang bisa dihentikan-dilanjutkan, dan Ractor untuk paralelisme sejati yang aman dari race condition.

Episode 11 ini membedah ketiganya secara bertahap. Kita mulai dari Thread dengan Mutex dan Queue, lalu Fiber dengan scheduler untuk I/O async, dan diakhiri Ractor beserta API Ractor::Port yang baru di Ruby 4.0. Ingat kembali penjelasan GVL di episode 2: thread Ruby concurrent untuk I/O, tetapi hanya Ractor yang benar-benar parallel untuk CPU.

Thread dan Race Condition

Threading Dasar

Thread.new membuat thread baru yang berjalan bersamaan. Method join menunggu thread selesai — tanpanya, program utama bisa berakhir sebelum thread bekerja:

RubyThreading dasar
threads = 3.times.map do |i|
  Thread.new do
    sleep(0.1)
    puts "Thread #{i} selesai"
  end
end
threads.each(&:join)
puts "Semua selesai"

Thread.new do ... end membuat thread yang mengeksekusi block. threads.each(&:join) memanggil join pada setiap thread, memastikan program utama menunggu semuanya. Tanpa join, output Semua selesai bisa muncul lebih dulu. Thread cocok untuk workload yang didominasi menunggu I/O seperti HTTP atau database.

Race Condition dan Mutex

Karena thread berbagi state, dua thread yang mengubah variabel yang sama secara bersamaan bisa menghasilkan nilai salah — inilah race condition. Solusinya adalah Mutex (mutual exclusion) yang memastikan hanya satu thread di satu waktu:

RubyRace condition dengan Mutex
counter = 0
mutex = Mutex.new
threads = 10.times.map do
  Thread.new do
    10_000.times do
      mutex.synchronize { counter += 1 }
    end
  end
end
threads.each(&:join)
puts counter

Tanpa mutex.synchronize, hasil counter hampir pasti kurang dari 100.000 karena operasi += tidak atomik. Dengan Mutex, hasilnya tepat 100.000. Monitor adalah variasi yang bisa di-reentrant; Queue dari thread-safe menyediakan antrian aman untuk komunikasi antar thread.

Queue dan Nilai Kembalian

Queue memungkinkan thread berbagi data secara aman: satu thread menulis, yang lain membaca dengan pop yang memblokir sampai data tersedia:

RubyQueue antar thread
queue = Queue.new
Thread.new { 5.times { |i| queue << i } }
puts queue.pop
puts queue.pop

queue << i memasukkan data, dan queue.pop mengeluarkannya. Pemanggilan pop yang kedua menunggu data berikutnya tersedia — tidak ada busy loop yang membuang CPU. Nilai kembalian thread bisa diambil dengan thread.value, yang juga menunggu thread selesai.

Fiber dan Scheduler

Cooperative Concurrency

Fiber adalah unit eksekusi ringan yang berhenti dan dilanjutkan secara eksplisit — disebut cooperative karena perpindahan kendali diatur oleh programmer, bukan sistem operasi. Fiber.yield menunda fiber, resume melanjutkannya:

RubyFiber dasar
fiber = Fiber.new do
  puts "mulai"
  Fiber.yield "di tengah"
  puts "lanjut"
  "selesai"
end
 
puts fiber.resume
puts fiber.resume
puts fiber.resume

fiber.resume pertama menjalankan sampai Fiber.yield dan mengembalikan "di tengah". Resume kedua melanjutkan dari titik terakhir dan mengembalikan "selesai". Fiber jauh lebih ringan daripada thread — ribuan fiber bisa hidup dalam satu thread.

Fiber.scheduler dan I/O Async

Fiber.scheduler adalah interface yang memungkinkan I/O blok (seperti membaca socket) ditangguhkan dan ditangani asinkron dalam satu thread. Scheduler disuntikkan dengan Fiber.set_scheduler, dan library seperti async memanfaatkannya untuk menangani ribuan koneksi konkuren dengan overhead kecil. Inilah rahasia server Ruby berperforma tinggi yang menangani banyak koneksi jaringan.

Ractor: Paralelisme Sejati

Ractor.new dan Model Isolasi

Ractor (Ruby Actor) mengeksekusi kode secara benar-benar paralel di luar batasan GVL. Keamanannya dijamin oleh isolasi: objek tidak dibagikan antar ractor kecuali dinyatakan shareable. Membuat ractor sama seperti membuat thread, tetapi dengan isolasi data yang ketat:

RubyRactor dasar
rakt = Ractor.new { 21 * 2 }
rakt.join
puts rakt.value

rakt.join menunggu raktor selesai, dan rakt.value mengambil hasil perhitungan. Karena tidak ada state yang dibagikan, race condition yang kita lihat di Thread tidak mungkin terjadi di sini. Ractor inilah jawaban Ruby untuk memanfaatkan banyak core CPU.

Ractor::Port di Ruby 4.0

Ruby 4.0 memperkenalkan Ractor::Port — API komunikasi ractor baru yang menggantikan Ractor.yield dan Ractor.take. Komunikasi kini dipandang sebagai port: raktor berkomunikasi lewat kanal pesan yang eksplisit. Ractor.select memungkinkan satu raktor menunggu pesan dari banyak port sekaligus. Ractor.shareable_proc memastikan proc yang dikirim antar ractor bersifat immutable dan aman dibagikan.

Untuk workload parallel map, kombinasi raktor per item bekerja seperti ini:

RubyParallel map dengan Ractor
data = (1..4).to_a
hasil = data.map do |nilai|
  Ractor.new(nilai) { |n| n * n }
end.map(&:value)
puts hasil.inspect

Ractor.new(nilai) { |n| n * n } membungkus setiap komputasi dalam raktor yang berjalan paralel, lalu map(&:value) mengambil hasil masing-masing. Pada CPU multi-core, empat kuadrat dihitung bersamaan, bukan berurutan. Catatan: membuat terlalu banyak raktor punya overhead, jadi ukur dengan profiling — subjek episode 17.

Warning

Jangan mencampur state yang bisa diubah antar thread tanpa Mutex, dan jangan memindahkan objek non-shareable antar ractor. Kedua kesalahan ini menghasilkan bug yang paling sulit dideteksi — muncul hanya di production dengan beban tinggi.

Penutup

Episode 11 membuka dunia concurrency Ruby: Thread dengan Mutex, Queue, dan join, Fiber dengan Fiber.yield dan Fiber.scheduler untuk I/O async, serta Ractor untuk paralelisme sejati dengan API Ractor::Port baru di Ruby 4.0.

Inti yang harus dibawa pulang:

  • Thread concurrent untuk I/O; race condition dicegah dengan Mutex dan Queue.
  • thread.join dan thread.value menunggu dan mengambil hasil thread.
  • Fiber berjalan secara kooperatif dengan resume dan Fiber.yield.
  • Fiber.scheduler memungkinkan I/O asinkron dalam satu thread.
  • Ractor berjalan paralel di luar GVL dengan isolasi objek yang ketat.
  • Ruby 4.0 memperkenalkan Ractor::Port menggantikan Ractor.yield dan Ractor.take.
  • Ractor.select dan Ractor.shareable_proc melengkapi API komunikasi antar ractor.

Di episode 12 selanjutnya kita akan membahas RubyGems, Bundler, dan dependency managementgem install dan gem search, struktur gemspec, pembuatan gem dengan bundle gem, Gemfile dan bundle install, Gemfile.lock dengan checksum, source multi-gem, dan kelompok dependency. Di sinilah kalian belajar mengelola library seperti developer profesional.

Belajar Ruby - Concurrency & Parallelism (Thread, Fiber, Ractor) | Belajar Ruby