Очереди и стеки, разобранные Нами в прошлой записи, далеко не единственный метод общения между потоками. Для передачи данных можно также использовать каналы - более низкоуровневый механизм. Каналы, или трубы(от названия соответствующей функции/класса - pipe()), можно найти в системном модуле os.
Сначала изготовим такой скрипт:
| Python | 1
2
3
4
5
6
7
| import random
s1=random.randint(0,10)
s2=random.randint(0,10)
p=random.randint(0,10)
print(s1)
print(s2)
print('Done') |
|
Генерируем три числа, два из них выводим в стандартный поток и заканчиваем всё это надписью 'Done'. Ничего сложного. Теперь закроем этот скрипт и запомним где он находится. Дальше пишите команды где хотите - в отдельном файле или прямо в IDLE:
| Python | 1
2
3
4
| import os
pipe=os.popen('C:/Documents and Settings/Пользователь/Рабочий стол/script.py')
t=pipe.read()
print(t) |
|
Функция os.popen(name) создаёт канал, через который методом read() Мы читаем данные, напечатанные программой name в стандартном потоке. В результате Вы должны получить что-то вроде:
Где '\n' - символ перевода курсора на следующую строку. Возьмите на заметку, что Мы получили строку(str), в которой содержатся только те данные, которые Мы занесли в стандартный поток вывода с помощью print(), а это значит, что передать таким образом серьёзный объект невозможно. Ещё один нюанс: скрипт script.py запускается автоматически, в качестве дочернего потока.
Теперь пример посложнее, с другими трубами и с параллельными потоками:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| from threading import Thread
from time import sleep
import os
pout,pin=os.pipe()
def f1():
sleep(5)
s='Hello,man!'.encode()
os.write(pin,s)
def f2():
t=os.read(pout,64)
print(t.decode())
th=Thread(target=f1,args=())
th2=Thread(target=f2,args=())
th.start()
th2.start()
input() |
|
Разберём поподробнее:
В строке
Мы, грубо говоря, создаём два конца одного и того же канала - с одной стороны пишем, с другой читаем. Заметьте, что первая переменная pout - конец, с которого будет производиться чтение. Отпечатав pin и pout, Вы увидите коды дескрипторов - концов трубы(pipe()).
При передаче данных через каналы, не забывайте кодировать эти самые данные(encode(),decode()) - иначе передачи не произойдёт. Кроме того, функции read() вторым аргументом нужно передать примерный размер читаемой строки байтов.
Так же обратите внимание на то, что функция os.read() приостанавливает выполнение потока th2 до тех пор, пока не будут получены данные.
Однако всё это, так сказать, мелочи жизни. Взаимодействие потоков между собой, несомненно, вещь полезная, но гораздо актуальнее научиться связывать между собой отдельные процессы, которые, как Нам уже известно, являются более высокоуровневым механизмом выполнения. Можно догадаться, что нужный Нам инструмент находится в модуле multiprocessing. Смотрим:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| from multiprocessing import Pipe,Process
from time import sleep
import os
(pout,pin)=Pipe()
def f1(pin):
sleep(3)
s='Hello,man!'
pin.send(s)
def f2(pout):
t=pout.recv()
print(t)
p1=Process(target=f1,args=(pin,))
p2=Process(target=f2,args=(pout,))
if __name__=='__main__':
p1.start()
p2.start()
input() |
|
Точно так же берём два конца pin и pout. Правда на этот раз при конструировании процессов их(концы) нужно передать каждому экземпляру класса Process().
Не забывайте, что при передаче одного аргумента нужна приблудная запятая, которая даёт понять Python'у, что это кортеж(tuple), а не просто выделяющие скобки.
Так же Вы, должно быть, заметили, что посылаем данные Мы с помощью метода send()(а не write()), а вот принимаем не самым легкозапоминающимся методом recv().
При передаче объектов между процессами кодировать их не нужно! (encode(),decode())
|