Thread-local values let each thread keep its own context while sharing the same code path.

thread locals Use `Thread.current[...]` when each thread needs a private value that should not overwrite another thread's value.

Thread Local Context

prefix
thread_local_context.rb
Replay: real traced execution (multi-file project)
prefix = "req"
labels = []
lock = Mutex.new

workers = 2.times.map do |index|
  Thread.new do
    Thread.current[:label] = "#{prefix}-#{index + 1}"
    label = Thread.current[:label]

    lock.synchronize do
      labels << label
    end
  end
end

workers.each(&:join)

puts "prefix=#{prefix}"
puts "labels=#{labels.sort.join(",")}"
puts "count=#{labels.length}"
prefix = "job"
labels = []
lock = Mutex.new

workers = 2.times.map do |index|
  Thread.new do
    Thread.current[:label] = "#{prefix}-#{index + 1}"
    label = Thread.current[:label]

    lock.synchronize do
      labels << label
    end
  end
end

workers.each(&:join)

puts "prefix=#{prefix}"
puts "labels=#{labels.sort.join(",")}"
puts "count=#{labels.length}"
prefix = "task"
labels = []
lock = Mutex.new

workers = 2.times.map do |index|
  Thread.new do
    Thread.current[:label] = "#{prefix}-#{index + 1}"
    label = Thread.current[:label]

    lock.synchronize do
      labels << label
    end
  end
end

workers.each(&:join)

puts "prefix=#{prefix}"
puts "labels=#{labels.sort.join(",")}"
puts "count=#{labels.length}"
  1. prefix ← req, labels ← [], lock ← ⟨Thread::Mutex A⟩

    1prefix→ req = "req"  #@prefix="job", "task"2labels→ [] = []3lock→ ⟨Thread::Mutex A⟩ = Mutex.new45workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  2. do |index|

    pass 1 of 2
    5workers = 2.times.map do |index0|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  3. workers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]

    pass 2 of 2
    5workers→ [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩] = 2.times.map do |index1|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  4. Thread.current[:label] ← req-1, label ← req-1

    pass 1 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ req-1 = "#{prefixreq}-#{index0 + 1}"8    label→ req-1 = Thread.current[:label]req-1910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  5. do

    pass 1 of 2
    10lock.synchronize do11  labels[] << labelreq-112end
  6. Thread.current[:label] ← req-2, label ← req-2

    pass 2 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ req-2 = "#{prefixreq}-#{index1 + 1}"8    label→ req-2 = Thread.current[:label]req-2910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  7. do

    pass 2 of 2
    10lock.synchronize do11  labels["req-1"] << labelreq-212end
  8. workers.each(&:join)

    16workers.each(&:join)[⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 dead⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 dead⟩]1718puts "prefix=#{prefixreq}"19puts "labels=#{labels.sort.join(",")req-1,req-2}"20puts "count=#{labels.length2}"
    outputprefix=req
    labels=req-1,req-2
    count=2
  1. prefix ← job, labels ← [], lock ← ⟨Thread::Mutex A⟩

    1prefix→ job = "job"2labels→ [] = []3lock→ ⟨Thread::Mutex A⟩ = Mutex.new45workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  2. do |index|

    pass 1 of 2
    5workers = 2.times.map do |index0|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  3. workers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]

    pass 2 of 2
    5workers→ [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩] = 2.times.map do |index1|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  4. Thread.current[:label] ← job-1, label ← job-1

    pass 1 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ job-1 = "#{prefixjob}-#{index0 + 1}"8    label→ job-1 = Thread.current[:label]job-1910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  5. do

    pass 1 of 2
    10lock.synchronize do11  labels[] << labeljob-112end
  6. Thread.current[:label] ← job-2, label ← job-2

    pass 2 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ job-2 = "#{prefixjob}-#{index1 + 1}"8    label→ job-2 = Thread.current[:label]job-2910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  7. do

    pass 2 of 2
    10lock.synchronize do11  labels["job-1"] << labeljob-212end
  8. workers.each(&:join)

    16workers.each(&:join)[⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 dead⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 dead⟩]1718puts "prefix=#{prefixjob}"19puts "labels=#{labels.sort.join(",")job-1,job-2}"20puts "count=#{labels.length2}"
    outputprefix=job
    labels=job-1,job-2
    count=2
  1. prefix ← task, labels ← [], lock ← ⟨Thread::Mutex A⟩

    1prefix→ task = "task"2labels→ [] = []3lock→ ⟨Thread::Mutex A⟩ = Mutex.new45workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  2. do |index|

    pass 1 of 2
    5workers = 2.times.map do |index0|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  3. workers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]

    pass 2 of 2
    5workers→ [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩] = 2.times.map do |index1|6  Thread.new do7    Thread.current[:label] = "#{prefix}-#{index + 1}"8    label = Thread.current[:label]910    lock.synchronize do11      labels << label12    end13  end14end
  4. Thread.current[:label] ← task-1, label ← task-1

    pass 1 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ task-1 = "#{prefixtask}-#{index0 + 1}"8    label→ task-1 = Thread.current[:label]task-1910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  5. do

    pass 1 of 2
    10lock.synchronize do11  labels[] << labeltask-112end
  6. Thread.current[:label] ← task-2, label ← task-2

    pass 2 of 2
    5workers = 2.times.map do |index|6  Thread.new do7    Thread.current[:label]→ task-2 = "#{prefixtask}-#{index1 + 1}"8    label→ task-2 = Thread.current[:label]task-2910    lock⟨Thread::Mutex A⟩.synchronize do11      labels << label12    end13  end14end
  7. do

    pass 2 of 2
    10lock.synchronize do11  labels["task-1"] << labeltask-212end
  8. workers.each(&:join)

    16workers.each(&:join)[⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 dead⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 dead⟩]1718puts "prefix=#{prefixtask}"19puts "labels=#{labels.sort.join(",")task-1,task-2}"20puts "count=#{labels.length2}"
    outputprefix=task
    labels=task-1,task-2
    count=2