Concurrency and Modules
Thread Local Context
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
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}"
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 end14enddo |index|
pass 1 of 25workers = 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 end14endworkers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]
pass 2 of 25workers→ [⟨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 end14endThread.current[:label] ← req-1, label ← req-1
pass 1 of 25workers = 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 end14enddo
pass 1 of 210lock.synchronize do11 labels[] << labelreq-112endThread.current[:label] ← req-2, label ← req-2
pass 2 of 25workers = 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 end14enddo
pass 2 of 210lock.synchronize do11 labels["req-1"] << labelreq-212endworkers.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
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 end14enddo |index|
pass 1 of 25workers = 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 end14endworkers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]
pass 2 of 25workers→ [⟨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 end14endThread.current[:label] ← job-1, label ← job-1
pass 1 of 25workers = 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 end14enddo
pass 1 of 210lock.synchronize do11 labels[] << labeljob-112endThread.current[:label] ← job-2, label ← job-2
pass 2 of 25workers = 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 end14enddo
pass 2 of 210lock.synchronize do11 labels["job-1"] << labeljob-212endworkers.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
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 end14enddo |index|
pass 1 of 25workers = 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 end14endworkers ← [⟨Thread B /tmp/execution/⟨tmp D⟩.rb:57 run⟩, ⟨Thread C /tmp/execution/⟨tmp D⟩.rb:57 run⟩]
pass 2 of 25workers→ [⟨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 end14endThread.current[:label] ← task-1, label ← task-1
pass 1 of 25workers = 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 end14enddo
pass 1 of 210lock.synchronize do11 labels[] << labeltask-112endThread.current[:label] ← task-2, label ← task-2
pass 2 of 25workers = 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 end14enddo
pass 2 of 210lock.synchronize do11 labels["task-1"] << labeltask-212endworkers.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