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.
mutex = Mutex.new
mutex.lock
mutex.unlock
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
which means other threads need to wait the current thread executes the code in synchronize
block.
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
.
Generic Mutex Subsystem — Linux Kernel’s document about Mutex.