В вашем примере мьютекса, поток создается в методе b
спит на некоторое время, печатает b
затем пытается разблокировать мьютекс. Это не является законным, поток не может разблокировать мьютекс, если он уже не считает, что замок и поднимает ThreadError, если вы попробуете:
m = Mutex.new
m.unlock
результаты:
release.rb:2:in `unlock': Attempt to unlock a mutex which is not locked (ThreadError)
from release.rb:2:in `<main>'
Вы не увидите в вашем примере, потому что by default Ruby silently ignores exceptions raised in threads other than the main thread. Вы можете изменить это с помощью Thread::abort_on_exception=
- если добавить
Thread.abort_on_exception = true
в верхней части файла, вы увидите что-то вроде:
a
b
with-mutex.rb:15:in `unlock': Attempt to unlock a mutex which is not locked (ThreadError)
from with-mutex.rb:15:in `block in b'
(вы можете увидеть больше, чем один a
, но там будет только один b
).
В способе a
вы создаете потоки, которые приобретают блокировку, печатайте a
, вызывайте другой метод (который создает новый поток и сразу возвращает его), а затем завершается. Он не кажется хорошо документированным, но когда поток завершает работу, он выпускает любые блокировки, которые он имеет, поэтому в этом случае блокировка освобождается почти сразу, чтобы запускать другие потоки a
.
В целом замок не имеет большого эффекта. Он не предотвращает запуск потоков b
, и хотя он предотвращает два потока a
, работающих одновременно, он освобождается, как только поток, удерживающий его, выходит.
Думаю, вы могли бы подумать о semaphores, а в то время как документы Ruby “Mutex implements a simple semaphore” - это not quite the same.
Ruby не предоставляет семафоров в стандартной библиотеке, но предоставляет condition variables. (Эта ссылка относится к старым документам 2.0.0. Стандартная библиотека thread
по умолчанию требуется в Ruby 2.1+, и, похоже, это привело к тому, что текущие документы не были доступны.Также имейте в виду, что Ruby также имеет отдельную библиотеку monitor, которая (я думаю) добавляет те же функции (мьютексы и переменные условия) более объектно-ориентированным образом.)
Используя переменные условий и мьютексы, вы можете контролировать координацию между потоки. Uri Agassi’s answer показывает один из возможных способов сделать это (хотя я думаю, что есть состояние гонки с тем, как его решение начинается).
Если вы посмотрите на source for Queue (опять же это ссылка на 2.0.0 - библиотека потоков была преобразована в C в последних версиях, а в Ruby-версии проще), вы можете увидеть, что она реализована с помощью Mutex
es и ConditionVariables
. Когда вы вызываете $queue.pop
в потоке a
в очереди, вы в конечном итоге вызываете wait
on the mutex так же, как ответ Ури Агасси вызывает $cv.wait($mutex)
по его методу a
. Аналогичным образом, когда вы вызываете $queue << true
в свою b
, вы заканчиваете calling signal
on the condition variable так же, как звонки Ури Агасси в его b
нить.
Основная причина, по которой ваш пример блокировки файлов не работает, заключается в том, что блокировка файлов обеспечивает способ для нескольких процессов для координации друг с другом (как правило, так, что только один пытается записать в файл в одно и то же время) и doesn Помогите с координацией нитей в процессе. Код блокировки файла структурирован аналогично примеру мьютекса, поэтому, вероятно, он будет испытывать те же проблемы.
@sawa посмотрите в этом вопросе – fotanus
Aw, я не получил щедрость? Рад помочь. – Kache