Язык программирования Python
Шрифт:
startДает потоку жизнь.
runЭтот метод представляет действия, которые должны быть выполнены в потоке.
join([timeout])Поток, который вызывает этот метод, приостанавливается, ожидая завершения потока, чей метод вызван. Параметр timeout (число с плавающей точкой) позволяет указать время ожидания (в секундах), по истечении которого приостановленный поток продолжает свою работу независимо от завершения потока, чей метод join был вызван. Вызывать join некоторого потока можно много раз. Поток не может вызвать метод join самого себя. Также нельзя ожидать завершения еще не запущенного потока. Слово «join»
getNameВозвращает имя потока. Для главного потока это «MainThread».
setName(name)Присваивает потоку имя name.
isAliveВозвращает истину, если поток работает (метод run уже вызван, но еще не завершился).
isDaemonВозвращает истину, если поток имеет признак демона. Программа на Python завершается по завершении всех потоков, не являющихся демонами. Главный поток демоном не является.
setDaemon(daemonic)Устанавливает признак daemonic того, что поток является демоном. Начальное значение этого признака заимствуется у потока, запустившего данный. Признак можно изменять только для потоков, которые еще не запущены.
В модуле Thread пока что не реализованы возможности, присущие потокам в Java (определение групп потоков, приостановка и прерывание потоков извне, приоритеты и некоторые другие вещи), однако они, скорее всего, будут созданы в недалеком будущем.
Таймер
Класс threading.Timer представляет действие, которое должно быть выполнено через заданное время. Этот класс является подклассом класса threading.Thread, поэтому запускается также методом start. Следующий простой пример, печатающий на стандартном выводе Hello, world! поясняет сказанное:
Листинг
def hello:
print «Hello, world!»
t = Timer(30.0, hello)
t.start
Замки
Простейший замок может быть реализован на основе класса Lock модуля threading. Замок имеет два состояния: он может быть или открыт, или заперт. В последнем случае им владеет некоторый поток. Объект класса Lock имеет следующие методы:
acquire([blocking=True])Делает запрос на запирание замка. Если параметр blocking не указан или является истиной, то поток будет ожидать освобождения замка. Если параметр не был задан, метод не возвратит значения. Если blocking был задан и истинен, метод возвратит True (после успешного овладения замком). Если блокировка не требуется (то есть задан blocking=False), метод вернет True, если замок не был заперт и им успешно овладел данный поток. В противном случае будет возвращено False.
releaseЗапрос на отпирание замка.
lockedВозвращает текущее состояние замка (True — заперт, False — открыт). Следует иметь в виду, что даже если состояние замка только что проверено, это не означает, что он сохранит это состояние до следующей команды.
Имеется еще один вариант замка — threading.RLock, который отличается от threading.Lock тем, что некоторый поток может запрашивать его запирание много раз. Отпирание такого замка должно происходить столько же раз, сколько было запираний. Это может быть полезно, например, внутри рекурсивных функций.
Когда нужны замки?
Замки позволяют ограничивать вход в некоторую область
Тупиковая ситуация (deadlock)
Замки применяются для управления доступом к ресурсу, который нельзя использовать совместно. В программе таких ресурсов может быть несколько. При работе с замками важно хорошо продумать, не зайдет ли выполнение программы в тупик (deadlock) из–за того, что двум потокам потребуются одни и те же ресурсы, но ни тот, ни другой не смогут их получить, так как они уже получили замки. Такая ситуация проиллюстрирована в следующем примере:
Листинг
import threading, time
resource = {'A': threading.Lock, 'B': threading.Lock}
def proc(n, rs):
for r in rs:
print «Процесс %s запрашивает ресурс %s» % (n, r)
resource[r].acquire
print «Процесс %s получил ресурс %s» % (n, r)
time.sleep(1)
print «Процесс %s выполняется» % n
for r in rs:
resource[r].release
print «Процесс %s закончил выполнение» % n
p1 = threading.Thread(target=proc, name=«t1», args=[«1», «AB»])
p2 = threading.Thread(target=proc, name=«t2», args=[«2», «BA»])
p1.start
p2.start
p1.join
p2.join
В этом примере два потока (t1 и t2) запрашивают замки к одним и тем же ресурсам (A и B), но в разном порядке, отчего получается, что ни у того, ни у другого не хватает ресурсов для дальнейшей работы, и они оба безнадежно повисают, ожидая освобождения нужного ресурса. Благодаря операторам print можно увидеть последовательность событий:
Листинг
Процесс 1 запрашивает ресурс A
Процесс 1 получил ресурс A
Процесс 2 запрашивает ресурс B
Процесс 2 получил ресурс B
Процесс 1 запрашивает ресурс B
Процесс 2 запрашивает ресурс A
Существуют методики, позволяющие избежать подобных тупиков, однако их рассмотрение не входит в рамки данной лекции. Можно посоветовать следующие приемы:
построить логику приложения так, чтобы никогда не запрашивать замки к двум ресурсам сразу. Возможно, придется определить составной ресурс. В частности, к данному примеру можно было бы определить замок «AB» для указания эксклюзивного доступа к ресурсам A и B.