Consider this Ruby code that creates two threads incrementing a shared counter without synchronization. What will be the output?
counter = 0 threads = [] 2.times do threads << Thread.new do 1000.times { counter += 1 } end end threads.each(&:join) puts counter
Think about what happens when two threads update the same variable without locks.
Because the threads update counter without synchronization, some increments can be lost due to race conditions. So the final value is usually less than 2000.
Why does the standard Ruby interpreter (MRI) use a Global Interpreter Lock (GIL)?
Think about how MRI manages thread safety internally.
The GIL ensures only one thread executes Ruby code at a time, preventing race conditions inside the interpreter but limiting true parallelism.
Given this Ruby code using fibers, what will be printed?
fiber1 = Fiber.new do puts 'Fiber 1 start' Fiber.yield puts 'Fiber 1 resume' end fiber2 = Fiber.new do puts 'Fiber 2 start' Fiber.yield puts 'Fiber 2 resume' end fiber1.resume fiber2.resume fiber1.resume fiber2.resume
Remember that Fiber.yield pauses the fiber and resume continues it.
The fibers start and yield in order, then resume in the same order, producing the output in option D.
Look at this Ruby code snippet. Why does it raise a ThreadError?
thread = Thread.new do sleep 1 end thread.run thread.run
Check Ruby docs for Thread#run behavior.
Calling Thread#run twice on the same thread raises ThreadError because the thread cannot be restarted while running.
You want to increment a shared counter from multiple Ruby threads safely. Which code snippet correctly uses synchronization to avoid race conditions?
Think about locking only the increment operation, not the whole loop.
Option A locks each increment individually, preventing race conditions while allowing concurrency. Option A locks the entire 1000 increments per thread, serializing the work unnecessarily. Options A and B do not use synchronization and risk race conditions.