Implementing a synchronization barrier in Ruby
Let's implement a synchronization barrier. It has to know the number of threads it will handle, n, up front. During first n - 1 calls to sync
the barrier will cause a calling thread to wait. The call number n will wake all threads up.
class Barrier
def initialize(count)
@mutex = Mutex.new
@cond = ConditionVariable.new
@count = count
end
def sync
@mutex.synchronize do
@count -= 1
if @count > 0
@cond.wait @mutex
else
@cond.broadcast
end
end
end
end
Whole body of sync
is a critical section, i.e. it cannot be executed by two threads concurrently. Hence the call to Mutex#synchronize
.
When the decreased value of @count
is positive the thread is frozen. Passing the mutex as an argument to the call to ConditionVariable#wait
is critical to prevent deadlocks. It causes the mutex to be unlocked before freezing the thread.
A simple experiment starts 1k threads and makes them add elements to an array. Firstly they add zeros, then they synchronize and add ones. The expected result is a sorted array with 2k elements, of which 1k are zeros and 1k are ones.
mtx = Mutex.new
arr = []
num = 1000
barrier = Barrier.new num
num.times.map do
Thread.start do
mtx.synchronize { arr << 0 }
barrier.sync
mtx.synchronize { arr << 1 }
end
end .map &:join;
# Prints true. See it break by deleting `barrier.sync`.
puts [
arr.sort == arr,
arr.count == 2 * num,
arr.count(&:zero?) == num,
arr.uniq == [0, 1],
].all?
As a matter of fact, there's a gem named barrier which does exactly what I described above.
On a final note, don't use sleep for waiting in such circumstances. It's called busy waiting and is considered a bad practice.
Zero permit semaphores?
Again from The Little Book of Semaphores, §2.2:
Listing 2.1: Semaphore initialization syntax
fred = Semaphore(1)
The function
Semaphore
is a constructor; it creates and returns a new Semaphore. The initial value of the semaphore is passed as a parameter to the constructor.
So in the author's pseduocode, 0
isn't the number of permits; it's the initial value of the semaphore. What does a value of zero mean? It's explained in the text immediately proceeding listing 2.1:
If the value is positive, then it represents the number of threads that
can decrement without blocking. If it is negative, then it represents the number
of threads that have blocked and are waiting. If the value is zero, it means there
are no threads waiting, but if a thread tries to decrement, it will block.
(emphases added)
Related Topics
Rails - Aciverecord Use :Dependent => :Destroy on Condition
/Usr/Bin/Env Ruby_Noexec_Wrapper Fails with No File or Directory
Implementing a Synchronization Barrier in Ruby
Rails 3.2 Activeadmin 'Collection Is Not a Paginated Scope.' Error
Grabbing Snapshots from Webcams in Ruby
Xlsx Compressed by Rubyzip Not Readable by Excel
Automatic Associations in Ruby on Rails Fixtures
I = True and False in Ruby Is True
Deploy a Shell Script with Ruby Gem and Install in Bin Directory
How to Use a Named Scope in My Model Against an Array of Items
How to Extend Redcarpet to Support Auto Linking User Mentions
Ubuntu 12.10 - Ruby Gem Rmagick Missing Dependency Issue
Ruby on Rails: How to Use Oauth2::Accesstoken.Post
How to I Add a Hyperlink to a Cell in Axlsx
How to Search Array Through Ransack Gem
Parsing Date from Text Using Ruby