Как исключения из рубина вызывают разблокировку мутантов? - PullRequest
5 голосов
/ 30 сентября 2011

Недавно я работал с нитями Руби и обнаружил немного неожиданное поведение. В критическом разделе вызов raise вызывает освобождение мьютекса. Я мог бы ожидать этого от метода synchronize с его блоком, но, похоже, это также происходит, когда lock и unlock вызываются отдельно.

Например, код ниже выводит:

$ ruby testmutex.rb 
x sync
y sync

... где я ожидал бы, что y будет заблокирован до тепловой смерти вселенной.

m = Mutex.new


x = Thread.new() do
  begin
    m.lock
      puts "x sync"
      sleep 5
      raise "x err"
      sleep 5
    m.unlock 
  rescue 
  end
end


y = Thread.new() do
  sleep 0.5
  m.lock
    puts "y sync"
  m.unlock 
end


x.join
y.join

Почему разрешено выполнение потока y, даже если m.unlock в потоке x никогда не выполняется?

1 Ответ

3 голосов
/ 30 сентября 2011

Обратите внимание: если вы удалите raise и unlock из x, то поведение будет таким же.Таким образом, у вас есть ситуация, когда поток x блокирует мьютекс, а затем поток заканчивается, и мьютекс разблокируется.

m = Mutex.new
Thread.new{ m.lock; p m.locked? }.join
#=> true

p m.locked?
#=> false

Таким образом, мы видим, что ситуация не связана с raise.Поскольку у вас есть блок begin/rescue вокруг вашего raise, вы просто выходите из потока x на 5 секунд раньше, чем в противном случае.

Предположительно, интерпретатор отслеживает любые мьютексы, заблокированные потоком, иавтоматически и намеренно разблокирует их, когда поток умирает.(Однако я не могу подтвердить это проверкой исходного кода. Это всего лишь предположение, основанное на поведении.)

...