sleep(rand(0)) # Спать в течение случайно выбранного времени
# (меньше секунды).
x = 3
end
sleep(rand(0))
puts x
# Если запустить эту программу несколько раз подряд, то может быть
# напечатано как 1, так и 3!
У метода
new
есть синоним
fork
— это имя выбрано по аналогии с хорошо известным системным вызовом в UNIX.
13.1.2.
Доступ к локальным переменным потока
Мы знаем об опасности доступа из потока к переменным, определенным вне его области видимости, но мы также знаем, что у потока могут быть локальные данные. А что делать, если поток хочет «обнародовать» часть принадлежащих ему данных?
Для этой цели предусмотрен специальный механизм. Если объект
Thread
рассматривать как хэш, то к локальным данным потока можно обратиться из любого места в области видимости этого объекта. Мы не хотим сказать, что так можно обратиться к настоящим локальным переменным; это допустимо лишь для доступа к именованным данным, своим для каждого потока.
Существует также метод
key?
, который сообщает, используется ли указанное имя в данном потоке.
Внутри потока к таким данным тоже следует обращаться, как к хэшу. Метод
Thread.current
позволяет сделать запись чуть менее громоздкой.
thread = Thread.new do
t = Thread.current
t[:var1] = "Это строка"
t[:var2] = 365
end
# Доступ к локальным данным потока извне...
x = thread[:var1] # "Это строка"
y = thread[:var2] # 365
has_var2 = thread.key?("var2") # true
has_var3 = thread.key?("var3") # false
Отметим, что эти данные доступны другим потокам даже после того, их владелец завершил работу (как в данном случае).
Помимо символа (см. выше), для идентификации локальной переменной потока можно употреблять и строки.
thread = Thread.new do
t = Thread.current
t["var3"] = 25
t[:var4] = "foobar"
end
a = thread[:var3] = 25
b = thread["var4"] = "foobar"
He путайте эти специальные имена с настоящими локальными переменными. В следующем фрагменте разница видна более отчетливо:
thread = Thread.new do
t = Thread.current
t["var3"] = 25
t[:var4] = "foobar"
var3 = 99 # Настоящие локальные переменные
var4 = "zorch" # (извне недоступны)
end
a = thread[:var3] # 25
b = thread["var4"] # "foobar"
И еще отметим, что ссылку на объект (на настоящую локальную переменную) внутри потока можно использовать
для сокращенной записи. Это справедливо, если вы сохраняете одну и ту же ссылку, а не создаете новую.
thread = Thread.new do
t = Thread.current
x = "nXxeQPdMdxiBAxh"
t[:my_message] = x
x.reverse!
x.delete! "x"
x.gsub!(/[A-Z]/,"")
# С другой стороны, присваивание создает новый объект,
# поэтому "сокращение" становится бесполезным...
end
а = thread[:my_message] # "hidden"
Ясно, что сокращение не будет работать и в том случае, когда вы имеете дело с объектами наподобие
Fixnum
, которые хранятся как непосредственные значения, а не ссылки.
13.1.3. Опрос и изменение состояния потока
В классе
Thread
есть несколько полезных методов класса. Метод
list
возвращает массив «живых» потоков, метод
main
возвращает ссылку на главный поток программы, который породил все остальные, а метод
current
позволяет потоку идентифицировать самого себя.
t1 = Thread.new { sleep 100 }
t2 = Thread.new do
if Thread.current == Thread.main
puts "Это главный поток." # HE печатается,
end
1.upto(1000) { sleep 0.1 }
end
count = Thread.list.size # 3
if Thread.list.include ?(Thread.main)
puts "Главный поток жив." # Печатается всегда!
end
if Thread.current == Thread.main
puts "Я главный поток." # Здесь печатается...
end
Методы
exit
,
pass
,
start
,
stop
и
kill
служат для управления выполнением потоков (как изнутри, так и извне):
# в главном потоке...
Thread.kill(t1) # Завершить этот поток.
Thread.pass # Передать управление t2.
t3 = Thread.new do
sleep 20
Thread.exit # Выйти из потока.
puts "Так не бывает!" # Никогда не выполняется.
end
Thread.kill(t2) # Завершить t2.
# Выйти из главного потока (все остальные тоже завершаются).
Thread.exit
Отметим, что не существует метода экземпляра
stop
, поэтому поток может приостановить собственное выполнение, но не выполнение другого потока.