Хэл Фултон - Программирование на языке Ruby
- Название:Программирование на языке Ruby
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2007
- Город:Москва
- ISBN:5-94074-357-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Хэл Фултон - Программирование на языке Ruby краткое содержание
Ruby — относительно новый объектно-ориентированный язык, разработанный Юкихиро Мацумото в 1995 году и позаимствовавший некоторые особенности у языков LISP, Smalltalk, Perl, CLU и других. Язык активно развивается и применяется в самых разных областях: от системного администрирования до разработки сложных динамических сайтов.
Книга является полноценным руководством по Ruby — ее можно использовать и как учебник, и как справочник, и как сборник ответов на вопросы типа «как сделать то или иное в Ruby». В ней приведено свыше 400 примеров, разбитых по различным аспектам программирования, и к которым автор дает обстоятельные комментарии.
Издание предназначено для программистов самого широкого круга и самой разной квалификации, желающих научиться качественно и профессионально работать на Ruby.
Программирование на языке Ruby - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
По этой и ряду других причин некоторые авторитеты вообще рекомендуют держаться подальше от многопоточного программирования. Действительно, такие программы сложны и подвержены ошибкам, которые трудно отлаживать. Но мы оставим читателю самому решать, когда стоит применять эту технику.
Проблемы, связанные с несинхронизированными потоками, хорошо известны. При одновременном доступе к глобальным данным со стороны нескольких потоков данные могут быть запорчены. Если один поток делает какое-то допущение о том, что успел выполнить другой поток, возможна гонка (race condition); обычно это приводит к «недетерминированному» коду, который дает разные результаты при каждом запуске. Наконец, существует опасность тупиковой ситуации, когда ни один поток не может продолжить выполнение, поскольку ожидает ресурс, занятый другим потоком. Код, написанный так, что ни одна из этих проблем не возникает, называется безопасным относительно потоков.
Не все в Ruby безопасно относительно потоков, но имеются методы синхронизации, которые позволяют контролировать доступ к переменным и ресурсам, защищать критические секции программы и избегать тупиковых ситуаций. Мы рассмотрим их в этой главе и проиллюстрируем на примерах.
13.1. Создание потоков и манипулирование ими
К числу основных операций над потоками относятся создание потока, передача ему входной информации и получение результатов, останов потока и т.д. Можно получить список запущенных потоков, опросить состояние потока и выполнить ряд других проверок.
Ниже представлен обзор основных операций.
13.1.1. Создание потоков
Создать поток просто: достаточно вызвать метод new и присоединить блок, который будет исполняться в потоке.
thread = Thread.new do
# Предложения, исполняемые в потоке...
end
Возвращаемое значение — объект типа Thread
. Главный поток программы может использовать его для управления вновь созданным потоком.
А если нужно передать потоку параметры? Достаточно передать их методу Thread.new
, который, в свою очередь, передаст их блоку.
a = 4
b = 5
с = 6
thread2 = Thread.new(а,b,с) do |a, x, y|
# Манипуляции с a, x и y.
end
# Если переменная а будет изменена новым потоком,
# то главный поток не получит об этом никакого уведомления.
Параметры блока, являющиеся ссылками на существующие переменные, практически неотличимы от самих переменных. Поэтому, например, переменная а
в каком-то смысле «опасна», что и отражено в комментарии.
Поток может также обращаться к переменным из той же области видимости, в которой был создан. Ясно, что без синхронизации это может стать источником проблем. Главный и любой другой поток могут изменять такую переменную независимо друг от друга, и результаты подобных действий непредсказуемы.
x = 1
y = 2
thread3 = Thread.new do
# Этот поток может манипулировать переменными x and y
# из внешней области видимости, но это не всегда безопасно.
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 "Главный поток жив." # Печатается всегда!
Интервал:
Закладка: