Bird
Raised Fist0
Rubyprogramming~10 mins

GIL (Global Interpreter Lock) impact in Ruby - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - GIL (Global Interpreter Lock) impact
Start Ruby Program
↓
Create Multiple Threads
↓
Each Thread Requests GIL
↓
Only One Thread Holds GIL
↓
Thread Executes Ruby Code
↓
Thread Releases GIL
↓
Next Thread Requests GIL
↓
Repeat Until All Threads Complete
↓
Program Ends
Ruby threads run one at a time inside the interpreter because only one thread can hold the GIL, so threads take turns executing Ruby code.
Execution Sample
Ruby
threads = []
5.times do |i|
  threads << Thread.new do
    puts "Thread #{i} start"
    sleep(1)
    puts "Thread #{i} end"
  end
end
threads.each(&:join)
This code creates 5 threads that print start and end messages with a sleep in between, showing how threads run with GIL.
Execution Table
StepThreadActionGIL StatusOutput
1Thread 0Requests GIL and startsThread 0 holds GILThread 0 start
2Thread 0Sleeps (GIL released during sleep)GIL released
3Thread 1Requests GIL and startsThread 1 holds GILThread 1 start
4Thread 1Sleeps (GIL released during sleep)GIL released
5Thread 2Requests GIL and startsThread 2 holds GILThread 2 start
6Thread 2Sleeps (GIL released during sleep)GIL released
7Thread 3Requests GIL and startsThread 3 holds GILThread 3 start
8Thread 3Sleeps (GIL released during sleep)GIL released
9Thread 4Requests GIL and startsThread 4 holds GILThread 4 start
10Thread 4Sleeps (GIL released during sleep)GIL released
11Thread 0Requests GIL and endsThread 0 holds GILThread 0 end
12Thread 1Requests GIL and endsThread 1 holds GILThread 1 end
13Thread 2Requests GIL and endsThread 2 holds GILThread 2 end
14Thread 3Requests GIL and endsThread 3 holds GILThread 3 end
15Thread 4Requests GIL and endsThread 4 holds GILThread 4 end
16-All threads finishedNo GIL holder-
💡 All threads completed their execution and the program ends.
Variable Tracker
VariableStartAfter Step 1After Step 3After Step 5After Step 7After Step 9After Step 11After Step 16
GIL HolderNoneThread 0Thread 1Thread 2Thread 3Thread 4Thread 0None
Thread 0 StateNot startedRunningSleepingSleepingSleepingSleepingFinishedFinished
Thread 1 StateNot startedNot startedRunningSleepingSleepingSleepingSleepingFinished
Thread 2 StateNot startedNot startedNot startedRunningSleepingSleepingSleepingFinished
Thread 3 StateNot startedNot startedNot startedNot startedRunningSleepingSleepingFinished
Thread 4 StateNot startedNot startedNot startedNot startedNot startedRunningSleepingFinished
Key Moments - 3 Insights
Why does only one thread run Ruby code at a time even though we created multiple threads?
Because of the GIL, only one thread can hold the lock and run Ruby code at a time, as shown in the execution_table where only one thread holds the GIL in each step.
Does the GIL block threads during sleep?
No, during sleep the thread releases the GIL, allowing other threads to run, which is why in the execution_table you see GIL released during sleep steps.
Why do threads take turns holding the GIL instead of running simultaneously?
The GIL is a lock that only one thread can hold, so threads must wait their turn to run Ruby code, as the execution_table shows threads requesting and releasing the GIL in sequence.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, which thread holds the GIL at step 5?
AThread 1
BThread 0
CThread 2
DThread 3
💡 Hint
Check the 'GIL Status' column at step 5 in the execution_table.
At which step does Thread 0 release the GIL after sleeping?
AStep 2
BStep 11
CStep 1
DStep 16
💡 Hint
Look for when Thread 0 sleeps and GIL is released in the execution_table.
If the GIL did not exist, how would the 'GIL Status' column change?
ANo thread would hold the GIL at any time
BMultiple threads would hold the GIL simultaneously
COnly one thread would still hold the GIL at a time
DThreads would never release the GIL
💡 Hint
Think about the purpose of the GIL shown in the variable_tracker and execution_table.
Concept Snapshot
GIL (Global Interpreter Lock) in Ruby means only one thread runs Ruby code at a time.
Threads take turns holding the GIL to execute.
During blocking operations like sleep, GIL is released.
This limits true parallel execution of Ruby threads.
Use processes or native extensions for parallelism.
Full Transcript
This visual execution shows how Ruby's Global Interpreter Lock (GIL) affects threads. Multiple threads are created, but only one thread can hold the GIL and run Ruby code at a time. When a thread sleeps, it releases the GIL so other threads can run. Threads take turns requesting and releasing the GIL until all finish. This means Ruby threads do not run Ruby code in parallel, but rather in sequence, sharing the GIL. The execution table tracks which thread holds the GIL and what output is produced at each step. The variable tracker shows thread states and GIL ownership over time. Key moments clarify why only one thread runs at once and how sleep releases the GIL. The quiz tests understanding of GIL ownership and behavior. Overall, the GIL limits Ruby thread parallelism inside the interpreter.

Practice

(1/5)
1.

What does the Global Interpreter Lock (GIL) in Ruby do?

easy
A. Automatically speeds up all multi-threaded Ruby programs
B. Allows multiple threads to run Ruby code simultaneously
C. Prevents any threads from running in Ruby
D. Allows only one thread to execute Ruby code at a time

Solution

  1. Step 1: Understand the role of GIL

    The GIL is a lock that ensures only one thread runs Ruby code at once.
  2. Final Answer:

    Allows only one thread to execute Ruby code at a time -> Option D
  3. Quick Check:

    GIL = single-thread execution [OK]
Hint: GIL means one thread runs Ruby code at a time [OK]
Common Mistakes:
  • Thinking GIL allows true parallel Ruby code execution
  • Believing GIL stops all threading
  • Assuming GIL speeds up multi-threading
2.

Which Ruby code snippet correctly starts two threads that print numbers?

Option A:
threads = [Thread.new { puts 1 }, Thread.new { puts 2 }]
threads.each(&:join)

Option B:
Thread.new { puts 1 }
Thread.new { puts 2 }

Option C:
threads = Thread.new { puts 1 }
threads = Thread.new { puts 2 }
threads.join

Option D:
Thread.start { puts 1 }
Thread.start { puts 2 }
easy
A. Creates two threads and waits for both to finish
B. Creates two threads but does not wait for them
C. Creates one thread twice, only waits for last
D. Uses undefined Thread.start method

Solution

  1. Step 1: Analyze thread creation and joining

    Creates two threads and waits for both to finish. Creates two threads and calls join on both, ensuring both finish.
  2. Final Answer:

    Creates two threads and waits for both to finish -> Option A
  3. Quick Check:

    Thread.new + join = wait for threads [OK]
Hint: Use join to wait for all threads to finish [OK]
Common Mistakes:
  • Not calling join and missing thread completion
  • Overwriting thread variables losing references
  • Using non-existent Thread.start method
3.

What will be the output of this Ruby code considering the GIL?

count = 0
threads = 5.times.map do
  Thread.new do
    1000.times { count += 1 }
  end
end
threads.each(&:join)
puts count
medium
A. 5000
B. 1000
C. May be less than 5000 due to race conditions
D. 0

Solution

  1. Step 1: Understand thread increments with GIL

    GIL allows one thread at a time, but count += 1 is not atomic and can cause race conditions.
  2. Final Answer:

    May be less than 5000 due to race conditions -> Option C
  3. Quick Check:

    Non-atomic increments + threads = race condition [OK]
Hint: GIL doesn't prevent race conditions on shared data [OK]
Common Mistakes:
  • Assuming GIL makes increments atomic
  • Expecting exact 5000 without synchronization
  • Ignoring thread interference on variables
4.

Identify the problem in this Ruby code using threads and fix it:

count = 0
threads = 3.times.map do
  Thread.new do
    100.times { count += 1 }
  end
end
threads.each(&:join)
puts count
medium
A. Add a Mutex to synchronize increments
B. Remove threads and run increments sequentially
C. Use Thread.kill to stop threads early
D. No problem, code is correct

Solution

  1. Step 1: Identify race condition on count variable

    Multiple threads increment count without synchronization causing race conditions.
  2. Final Answer:

    Add a Mutex to synchronize increments -> Option A
  3. Quick Check:

    Mutex prevents race conditions [OK]
Hint: Use Mutex to protect shared data in threads [OK]
Common Mistakes:
  • Ignoring race conditions on shared variables
  • Trying to kill threads instead of synchronizing
  • Assuming GIL handles all thread safety
5.

You want to speed up a CPU-heavy Ruby program using threads. Considering the GIL, what is the best approach?

hard
A. Disable the GIL in Ruby settings
B. Use multiple processes instead of threads
C. Use threads only for I/O tasks, not CPU tasks
D. Use more threads since GIL allows parallel CPU work

Solution

  1. Step 1: Understand GIL impact on CPU-bound threads

    GIL allows only one thread to run Ruby code at a time, so threads don't speed up CPU-heavy tasks.
  2. Final Answer:

    Use multiple processes instead of threads -> Option B
  3. Quick Check:

    Processes bypass GIL for CPU parallelism [OK]
Hint: Use processes, not threads, for CPU speedup in Ruby [OK]
Common Mistakes:
  • Believing threads run CPU tasks in parallel with GIL
  • Trying to disable GIL (not possible in standard Ruby)
  • Using threads for CPU tasks instead of I/O
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard