#14 dyvniy » Вт, 9 декабря 2014, 12:29:50
И снова потоки
http://www.cyberforum.ru/blogs/214240/blog784.html- Спойлер
- Python. Параллельные потоки. Часть 1.
Запись от Фаер размещена 30.09.2012 в 15:34
Обновил(-а) Фаер 08.09.2013 в 10:32
Метки python, потоки, _thread
Параллельные потоки - вещь полезная и даже интересная, но почувствовать всю их мощь можно, только работая с большими программами и граф. интерфейсами. Большие программы Мы рассматривать не будем, чтобы не забивать голову сторонними предметами, а поэкспериментируем с мелочью. Но сначала немного теории.
GIL(Global Interpreter Lock) - глобальная блокировка интерпретатора. Это понятие не слишком важно для тех, кто не хочет лезть в самую суть параллельных потоков, но кратко ознакомиться с ним всё-же не помешает. На самом деле, большая часть задач, выполняемых компьютером одновременно, не так уж и одновременна. В частности, из-за GIL Мы попадаем в такую ситуацию: в конкретный момент времени интерпретатор выполняет лишь один поток. Параллельность достигается за счёт быстрой смены выполняемых потоков, настолько быстрой (обычно каждые 0.05 сек.), что возникает ощущение одновременности выполнения операций. Таким образом, блокировка не позволяет использовать преимущества многопроцессорных систем, но это недоразумение можно обойти с помощью параллельных процессов, которые Мы разберём через урок.
Модуль _thread
Данный модуль позволяет запускать параллельный поток с помощью функции start_new_thread(). Поток, в Нашем случае, так же представляет собой функцию.
Код Python
Код: Выделить всё
import _thread
from time import sleep
def hel(): #описываем функцию, которую собираемся запустить параллельно основному потоку
sleep(2)
print('Hello,world!')
_thread.start_new_thread(hel,()) #запускаем функцию в качестве параллельного потока
sleep(5)
print('Done')
input()
Вот такой скрипт. Если запустить его, то через две секунды после старта Вы получите надпись Hello,world! и ещё через три(а не через 5, как было бы при вызове функции в этом же потоке) надпись Done.
Обратите внимание, функции start_new_thread() мы передаём два аргумента: непосредственно функцию, которую Мы хотим запустить в параллельном потоке, и кортеж аргументов, которые эта функция принимает. В Нашем случае, передавать какие-либо данные в функцию hel() не нужно.
После вызова start_new_thread() интерпретатор "параллельно" выполняет два потока:
1-ый продолжает выполнять команды, стоящие после start_new_thread()(основной поток).
2-ой выполняет команды, находящиеся в функции hel()(дочерний поток).
Время от времени Нам может требоваться подождать завершения одного из потоков. Как раз для таких случаев в модуле _thread имеются блокировки.
Код Python
Код: Выделить всё
import _thread
from time import sleep
l=_thread.allocate_lock()#создаём блокировку
def hel():
sleep(2)
print('Hello,world!')
l.acquire()#"замыкаем" блокировку в знак окончания выполнения потока
_thread.start_new_thread(hel,())
while not l.locked():
pass
sleep(2)
print('Done')
input()
При выполнении этого скрипта происходит следующее:
1. Запускается параллельный поток. Основной поток ждёт пока блокировка l не будет "замкнута"(l.locked()).
2. После окончания выполнения функции hel(), Мы "замыкаем" блокировку с помощью метода acquire().
3. Основной поток получает информацию о том, что параллельный поток был остановлен(l.locked()) и продолжает выполнять команды.
Но это ещё не всё. Выполнение потока можно приостановить, а потом продолжить. Для этого Нам понадобится та же самая блокировка.
Код Python
Код: Выделить всё
import _thread
from time import sleep
l=_thread.allocate_lock()
def fun():
while True:
if not l.locked():
print('Hello!')
sleep(1)
def by():
while True:
print('Bye')
sleep(1)
_thread.start_new_thread(fun,())
sleep(0.5)
_thread.start_new_thread(by,())
sleep(3)
l.acquire()
sleep(3.5)
l.release()
input()
Данный скрипт печатает две надписи поочереди. Через три секунды после запуска он блокирует(l.acquire()) один из потоков(fun()) и продолжает писать только одно слово. Ещё через три секунды выполнение заблокированного потока возобновляется(l.release()) и в консоли снова поочереди появляюся две надписи.
Кроме всего этого, поток можно завершить, передав соответствующую команду в нём же. Грубо говоря, поток завершит сам себя:
Код Python
Код: Выделить всё
import _thread
from time import sleep
def hel():
sleep(2)
_thread.exit()
print('Hello,world!')
_thread.start_new_thread(hel,())
sleep(5)
print('Done')
input()
В данном скрипте функция _thread.exit() завершает поток, в котором выполняется. Таким образом, надпись Hello,world! Вы не увидите, так как поток был прерван до того, как дело дошло до print('Hello,world!').