hesitate
@mutex.lock
if @hash[w]
hesitate
@hash[w] += ['#{listnum}:#{lnum}']
else
hesitate
@hash[w] = ['#{listnum}:#{lnum}']
end
@mutex.unlock
end
lnum += 1
end
end
t1 = Thread.new(0) {|num| process_list(num) }
t2 = Thread.new(1) {|num| process_list(num) }
t3 = Thread.new(2) {|num| process_list(num) }
t1.join
t2.join
t3.join
count = 0
@hash.values.each {|v| count += v.size }
puts 'Всего слов: #{count} ' # Всегда печатается 8!
Отметим, что помимо метода lock
в классе Mutex
есть также метод try_lock
. Он отличается от lock
тем, что если мьютекс уже захвачен другим потоком, то он не дожидается освобождения, а сразу возвращает false
.
require 'thread'
mutex = Mutex.new
t1 = Thread.new { mutex.lock; sleep 30 }
sleep 1
t2 = Thread.new do
if mutex.try_lock
puts 'Захватил'
else
puts 'He сумел захватить' # Печатается немедленно.
end
end
sleep 2
Эта возможность полезна, если поток не хочет приостанавливать выполнение. Есть также метод synchronize
, который захватывает мьютекс, а потом автоматически освобождает его.
mutex = Mutex.new
mutex.synchronize do
# Любой код, нуждающийся в защите...
end
Существует еще библиотека mutex_m
, где определен модуль Mutex_m
, который можно подмешивать к классу (или использовать для расширения объекта). У такого расширенного объекта будут все методы мьютекса, так что он сам может выступать в роли мьютекса.
require 'mutex_m'
class MyClass
include Mutex_m
# Теперь любой объект класса MyClass может вызывать
# методы lock, unlock, synchronize...
# Внешние объекты также могут вызывать эти
# методы для объекта MyClass.
end
13.2.3. Предопределенные классы синхронизированных очередей
В библиотеке thread.rb
есть пара классов, которые иногда бывают полезны. Класс Queue
реализует безопасную относительно потоков очередь, доступ к обоим концам которой синхронизирован. Это означает, что разные потоки могут, ничего не опасаясь, работать с такой очередью. Класс SizedQueue
отличается от предыдущего тем, что позволяет ограничить размер очереди (число элементов в ней).
Оба класса имеют практически один и тот же набор методов, поскольку SizedQueue
наследует Queue
. Правда, в подклассе определен еще акцессор max, позволяющий получить и установить максимальный размер очереди.
buff = SizedQueue.new(25)
upper1 = buff.max #25
# Увеличить размер очереди...
buff.max = 50
upper2 = buff.max # 50
В листинге 13.3 приведено решение задачи о производителе и потребителе. Для производителя задержка (аргумент sleep) чуть больше, чем для потребителя, чтобы единицы продукции «накапливались».
require 'thread'
buffer = SizedQueue.new(2)
producer = Thread.new do
item = 0
loop do
sleep rand 0
puts 'Производитель произвел #{item}'
buffer.enq item