Mutex (MUTual EXclusion) is a way to ensure only lock holder can use CPU. Others need to block and wait off-CPU. E.g., Multiple threads try to access shared array. You can use Mutex to protect a piece of work in multi-threaded world. Mutex make sure only one thread at any given time can access this code. Mutex can be implemented by library or Kernel.
mutex = Mutex.new
mutex.synchronize do
# guarantee only 1 thread can access this block
user.withdraw! 500
end
Unlike Monitor, you cannot have nested synchronize
block with Mutex.
Obtain/Release lock
mutex = Mutex.new
mutex.lock
You can also use #try_lock
and returns false
when this mutex is locked by another thread. There is also a #locked?
method to check if a mutex has been locked or not. After you finished using the mutex
, release it by: mutex.unlock
.
Trying to lock a locked mutex:
mutex = Mutex.new
mutex.lock
mutex.lock
ThreadError: deadlock; recursive locking
Trying to unlock a not locked mutex:
mutex = Mutex.new
mutex.unlock
ThreadError: Attempt to unlock a mutex which is not locked
and you can’t unlock the mutex in another thread:
mutex = Mutex.new
mutex.lock
Thread.new { mutex.unlock }.join
#<Thread:0x00007f8407b480b0 (pry):3 run> terminated with exception (report_on_exception is true):
(pry):12:in `unlock': Attempt to unlock a mutex which is locked by another thread/fiber (ThreadError)
I believe this
mutex.synchronize do
# ...
end
equals to
mutex = Mutex.new
begin
mutex.lock
# ...
ensure
mutex.unlock
end
Thread::ConditionVariable
Put thread(s) to sleep until certain condition and wake up threads when condition satisfies. Usually use together with Mutex. When a thread sleep, another thread can do work, and with the condition variable, we can avoid hot loop.
mutex = Mutex.new
resource = ConditionVariable.new
thread_a = Thread.new do
mutex.synchronize do
# Thread A needs the resource
resource.wait(mutex)
# Thread A can have the resource
end
end
thread_b = Thread.new do
mutex.synchronize do
# Thread B finished using the resource
resource.signal
end
end
Real-world example:
-
TimedStack uses
ConditionVariable
. - Sequel’s ThreadedConnectionPool
APIs: wait(mutex, timeout = nil)
(stop current thread, release the lock of Mutex), signal
(wake up a single thread that was waiting for the lock of Mutex), broadcast
(wake up all threads that were waiting for the lock of Mutex)
ConditionVariable
is an alias of Thread::ConditionVariable
.
Learn More
Generic Mutex Subsystem — Linux Kernel’s document about Mutex.