Contents
Ruby Threading
Ruby Introduction to Multi-threading
Multi-threading in Ruby is a powerful feature that enables the concurrent execution of different parts of a program, optimizing CPU usage. Each part of the program is called a thread, and threads are essentially lightweight processes within a larger process. A single-threaded program executes instructions sequentially, while a multi-threaded program can run multiple threads concurrently, utilizing multiple cores of a processor. This leads to reduced memory usage and improved performance compared to single-threaded programs.
Before Ruby version 1.9, Ruby used green threads, meaning thread scheduling was handled by Ruby’s interpreter. However, from Ruby 1.9 onwards, threading is handled by the operating system, making thread execution more efficient. Despite this improvement, two threads in the same Ruby application still cannot run truly concurrently.
In Ruby, a multi-threaded program can be created using the Thread
class. A new thread is usually created by calling a block using Thread.new
.
Creating Threads in Ruby
To create a new thread in Ruby, you can use any of three blocks: Thread.new
, Thread.start
, or Thread.fork
. The most commonly used is Thread.new
. Once a thread is created, the main (original) thread resumes execution after the thread creation block, running in parallel with the new thread.
Syntax:
# Main thread runs here
# Creating a new thread
Thread.new {
# Code to run inside the new thread
}
# Main thread continues executing
Example:
# Ruby program to demonstrate thread creation
def task1
count = 0
while count <= 2
puts "Task 1 - Count: #{count}"
sleep(1) # Pauses execution for 1 second
count += 1
end
end
def task2
count = 0
while count <= 2
puts "Task 2 - Count: #{count}"
sleep(0.5) # Pauses execution for 0.5 seconds
count += 1
end
end
# Creating threads for each task
thread1 = Thread.new { task1() }
thread2 = Thread.new { task2() }
# Ensuring the main program waits for threads to finish
thread1.join
thread2.join
puts "All tasks completed"
Output:
Task 1 - Count: 0
Task 2 - Count: 0
Task 2 - Count: 1
Task 1 - Count: 1
Task 2 - Count: 2
Task 1 - Count: 2
All tasks completed
Note: The exact output may vary depending on how the operating system allocates resources to the threads.
Terminating Threads
When the Ruby program finishes, all associated threads are also terminated. However, you can manually kill a thread using the Thread.kill
method.
Syntax:
Thread.kill(thread)
Thread Variables and Scope
Each thread has access to local, global, and instance variables within the scope of the block where it is defined. However, variables defined within a thread block are local to that thread and cannot be accessed by other threads. If multiple threads need to access the same variable concurrently, proper synchronization is required.
Example:
# Ruby program to demonstrate thread variables
# Global variable
$message = "Hello from Ruby!"
def task1
counter = 0
while counter <= 2
puts "Task 1 - Counter: #{counter}"
sleep(1)
counter += 1
end
puts "Global message: #{$message}"
end
def task2
counter = 0
while counter <= 2
puts "Task 2 - Counter: #{counter}"
sleep(0.5)
counter += 1
end
puts "Global message: #{$message}"
end
# Creating threads for each task
thread1 = Thread.new { task1() }
thread2 = Thread.new { task2() }
# Waiting for both threads to finish
thread1.join
thread2.join
puts "Program finished"
Output:
Task 1 - Counter: 0
Task 2 - Counter: 0
Task 2 - Counter: 1
Task 1 - Counter: 1
Task 2 - Counter: 2
Task 1 - Counter: 2
Global message: Hello from Ruby!
Global message: Hello from Ruby!
Program finished
In Ruby, threads are utilized to implement concurrent programming. Programs that require multiple threads use the Thread
class, which provides a variety of methods to handle thread-based operations.
Public Class Methods
1. abort_on_exception: This method returns the status of the global “abort on exception” setting. By default, its value is false
. If set to true
, all threads are aborted when an exception is raised in any of them.
Thread.abort_on_exception -> true or false
2. abort_on_exception=: This method sets the new state of the global “abort on exception” flag. When set to true
, threads are aborted when an exception arises. The return value is a boolean.
Thread.abort_on_exception= bool -> true or false
Example:
# Ruby program to demonstrate abort_on_exception
Thread.abort_on_exception = true
thread = Thread.new do
puts "Starting new thread"
raise "An error occurred in the thread"
end
sleep(0.5)
puts "Execution complete"
Output:
Starting new thread
RuntimeError: An error occurred in the thread
3. critical: Returns the current “thread critical” status. This flag indicates whether Ruby’s thread scheduler is in a critical section.
Thread.critical -> true or false
4. critical=: This method sets the global “thread critical” condition. When set to true
, it blocks scheduling of any thread but allows new threads to be created and run. Thread-critical sections are used mainly in threading libraries.
Thread.critical= bool -> true or false
5. current: This method returns the currently running thread.
Thread.current -> thread
6. exit: Terminates the current thread and schedules another thread to run. If the thread is marked to be killed, it will return the thread. If it’s the main thread or the last thread, the program will exit.
Thread.exit
7. fork: Similar to start
, this method initiates a new thread.
Thread.fork { block } -> thread
8. kill: This method terminates a specified thread.
Thread.kill(thread)
Example:
# Ruby program to demonstrate the kill method
counter = 0
# Create a new thread
thread = Thread.new { loop { counter += 1 } }
# Sleep for a short time
sleep(0.4)
# Kill the thread
Thread.kill(thread)
# Check if the thread is alive
puts thread.alive? # Output: false
9. list: This method returns an array of all the thread objects, whether they are runnable or stopped.
Thread.list -> array
Example:
# Ruby program to demonstrate list method
# First thread
Thread.new { sleep(100) }
# Second thread
Thread.new { 1000.times { |i| i*i } }
# Third thread
Thread.new { Thread.stop }
# List all threads
Thread.list.each { |thr| p thr }
Output:
#
#
#
10. main: This method returns the main thread of the process. Each run of the program will generate a unique thread ID.
Thread.main -> thread
Example:
# Ruby program to print the main thread's ID
puts Thread.main
Output:
#
11. new: Creates and runs a new thread. Any arguments passed are given to the block.
Thread.new([arguments]*) { |arguments| block } -> thread
12. pass: Attempts to pass execution to another thread. The actual switching depends on the operating system.
Thread.pass
13. start: Similar to new
. If the Thread
class is subclassed, calling start
from the subclass will not invoke the subclass’s initialize
method.
Thread.start([arguments]*) { |arguments| block } -> thread
14. stop: Stops the current thread, putting it to sleep and allowing another thread to be scheduled. It also resets the critical condition to false
.
Thread.stop
Example:
# Ruby program to demonstrate stop and pass methods
thread = Thread.new { print "Start"; Thread.stop; print "End" }
# Pass control to another thread
Thread.pass
print "Main"
thread.run
thread.join
Output:
StartMainEnd
Ruby Thread Class-Public Class Methods
In Ruby, threads are utilized to enable concurrent programming. Programs requiring multiple threads rely on the Thread
class to create and manage threads. The Thread
class offers a variety of methods to perform specific tasks.
Public Class Methods
1. abort_on_exception:
This method checks the global “abort on exception” setting and returns its current status. By default, this setting is false
. When set to true
, it ensures that all threads are terminated if an exception occurs in any thread.
Thread.abort_on_exception -> true or false
2. Thread.abort_on_exception=: This method determines if threads will abort when an exception occurs. If set to true
, any exception in a thread will terminate the program.
Example:
Thread.abort_on_exception = true
x = Thread.new do
puts "Hello from the thread!"
raise "Error raised in the thread"
end
sleep(0.5)
puts "This won't be printed"
Output:
Hello from the thread!
test.rb:6: Error raised in the thread (RuntimeError)
from test.rb:4:in `initialize'
from test.rb:4:in `new'
from test.rb:4
3. Thread.critical: This method retrieves or sets the global “thread critical” condition. When set to true
, thread scheduling is prohibited, but new threads can still be created and run.
Example:
Thread.critical = true
puts Thread.critical # Output: true
Thread.critical = false
puts Thread.critical # Output: false
Output:
true
false
4. Thread.current: Returns the currently executing thread.
Example:
puts Thread.current
Output:
#
5. Thread.exit
Terminates the currently running thread and schedules another thread to run.
Example:
x = Thread.new do
puts "Running thread"
Thread.exit
puts "This won't be executed"
end
x.join
puts "Thread exited"
Output:
Running thread
Thread exited
6. Thread.kill: Terminates the specified thread.
Example:
counter = 0
x = Thread.new { loop { counter += 1 } }
sleep(0.4)
Thread.kill(x)
sleep(0.5)
puts x.alive?
Output:
false
7. Thread.list: Returns an array of all threads (either runnable or stopped).
Example:
Thread.new { sleep(100) }
Thread.new { 10000.times {|i| i * i } }
Thread.new { Thread.stop }
Thread.list.each { |thr| p thr }
Output:
#
#
#
#
8. Thread.main: Returns the main thread of the process.
Example:
puts Thread.main
Output:
#
9. Thread.new: Creates and starts a new thread to execute a block of code.
Example:
t = Thread.new do
puts "Executing in a new thread"
end
t.join
Output:
Executing in a new thread
10. Thread.passAttempts to pass execution to another thread, depending on the operating system’s thread scheduler.
Example:
Thread.new { 3.times { puts "Running task"; Thread.pass } }
puts "Main thread"
Output:
Main thread
Running task
Running task
Running task
11. Thread.stop: Stops the currently running thread and schedules another thread.
Example:
x = Thread.new { print "Start"; Thread.stop; print "End" }
Thread.pass
print "Running Main Thread"
x.run
x.join
Output:
StartRunning Main ThreadEnd
Ruby Thread Life Cycle & Its States
The thread life cycle explains the progression of a thread from its creation to termination. A new thread can be created using Thread.new
, Thread.start
, or Thread.fork
. There’s no need to start a thread explicitly—it starts running automatically when CPU resources are available. The value returned by Thread.new
is a Thread
object. The Thread
class provides several methods to query and control thread behavior.
A thread executes the block of code passed to Thread.new
and terminates once the block finishes execution. The result of the last expression in the block is the thread’s value, which can be accessed using the value
method of the Thread
object. The value
method returns the result only if the thread has completed execution; otherwise, it does not return any value. If an exception is raised within a thread (other than the main thread), the thread terminates.
Thread States
Ruby provides five thread states, representing the thread’s current status. You can check a thread’s status using the alive?
and status
methods.
State | Return Value |
---|---|
Runnable | “run” |
Sleeping | “sleep” |
Aborting | “aborting” |
Terminated normally | false |
Terminated with exception | nil |
Example: Checking Thread Status:
counter = 0
# Create a new thread
x = Thread.new { loop { counter += 1 } }
# Check if the thread is alive
puts x.alive?
Output:
true
Main Thread
In Ruby, the main thread is the top-level thread under which all other threads are spawned. The Ruby interpreter runs until both the main thread and all child threads complete their execution. The Thread.main
method returns the main thread object. If an exception occurs in the main thread and is not caught, the interpreter will print an error message and exit. If an exception occurs in a non-main thread, the interpreter will terminate that particular thread.
Example:
# Ruby program to demonstrate the main thread
# Display the main thread
puts Thread.main
# Create a new thread
thread1 = Thread.new { sleep 100 }
# List all threads
Thread.list.each { |t| p t }
# Print the current thread
puts "Current thread: " + Thread.current.to_s
# Create another thread
thread2 = Thread.new { sleep 100 }
# List all threads again
Thread.list.each { |t| p t }
# Print the current thread
puts "Current thread: " + Thread.current.to_s
# Kill the first thread
Thread.kill(thread1)
# Pass execution to the next thread
Thread.pass
# Kill the second thread
Thread.kill(thread2)
# List all threads after killing
Thread.list.each { |t| p t }
# Exit the main thread
Thread.exit
Outputs:
#
#
#
Current thread: #
#
#
#
Current thread: #
#
#
Alternate Thread States: Pausing, Waking, and Killing
Threads are created in the runnable state and are ready to execute. A thread can pause itself by entering the sleeping state using methods like Thread.stop
or Kernel.sleep
. If Kernel.sleep
is called without an argument, the thread pauses indefinitely. When a time argument is provided, the thread resumes after the specified time expires and reenters the runnable state.
Paused threads can be resumed using methods like wakeup
and run
. These methods change the thread’s state from sleeping to runnable. The run
method also triggers the thread scheduler, potentially allocating CPU resources to the resumed thread. The wakeup
method resumes a specific thread without invoking the thread scheduler.
To terminate a thread, you can use the kill
, terminate
, or exit
methods. These methods place the thread into the “terminated normally” state.
Example: Pausing and Resuming Threads
t = Thread.new do
puts "Thread started"
sleep(1)
puts "Thread resumed"
end
puts "Pausing main thread"
Kernel.sleep(2)
t.run
t.join
Outputs:
Thread started
Pausing main thread
Thread resumed