Closing Channels in Ruby

# In this example we'll use a `jobs` queue to communicate work 
# to be done from the main thread to a worker thread. When we 
# have no more jobs for the worker we'll close the `jobs` queue.

require 'thread'

jobs = Queue.new
done = Queue.new

# Here's the worker thread. It repeatedly receives from `jobs`.
# We use `job = jobs.pop(true)` to get a job from the queue.
# If the queue is empty, it raises a ThreadError, which we use
# to determine when all jobs have been processed.

Thread.new do
  loop do
    begin
      job = jobs.pop(true)
      puts "received job #{job}"
    rescue ThreadError
      puts "received all jobs"
      done.push(true)
      break
    end
  end
end

# This sends 3 jobs to the worker over the `jobs` queue,
# then closes it.

(1..3).each do |j|
  jobs.push(j)
  puts "sent job #{j}"
end

puts "sent all jobs"

# We await the worker using the synchronization approach
# we saw earlier.

done.pop

# Checking if the queue is empty
puts "received more jobs: #{!jobs.empty?}"

To run the program:

$ ruby closing_queues.rb
sent job 1
sent job 2
sent job 3
sent all jobs
received job 1
received job 2
received job 3
received all jobs
received more jobs: false

In Ruby, we use Queue from the thread library to mimic the behavior of channels. The Queue class is thread-safe and can be used for inter-thread communication.

Instead of closing a channel, we use a ThreadError exception to signal that all jobs have been processed. This is because Ruby’s Queue doesn’t have a built-in closing mechanism like channels in some other languages.

The done queue is used for synchronization between the main thread and the worker thread, similar to the original example.

The concept of checking whether more jobs are available is implemented by checking if the jobs queue is empty after all jobs have been processed.

This example demonstrates how to use Ruby’s threading and queue mechanisms to achieve similar functionality to channels in other languages.