Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Внутри CPython гид по интерпретатору Python.pdf
Скачиваний:
2
Добавлен:
07.04.2024
Размер:
8.59 Mб
Скачать

254    Параллелизм и конкурентность

start = time.time()

host = "localhost" # Замените вашим хостом results = asyncio.run(scan(80, 100, host)) for result in results:

print("Port {0} is open".format(result))

print("Completed scan in {0} seconds".format(time.time() - start))

Сканирование занимает чуть более секунды:

$ python portscanner_async.py Port 80 is open

Completed scan in 1.0058400630950928 seconds

АСИНХРОННЫЕ ГЕНЕРАТОРЫ

Концепции генераторов и сопрограмм, с которыми вы познакомились к настоящему моменту, можно объединить в асинхронные генераторы.

Если функция объявляется с ключевым словом async и содержит оператор yield, при вызове она преобразуется в объект асинхронного генератора.

Асинхронные генераторы, как и обычные, должны выполняться конструкцией, поддерживающей протокол. Вместо __next__() асинхронные генераторы содержат метод __anext__().

Обычный цикл for не поймет асинхронный генератор, поэтому вместо него необходимо использовать команду async for.

Функцию check_port() можно преобразовать в асинхронный генератор, который через yield возвращает следующий открытый порт, пока не достигнет последнего порта или не найдет заданное количество открытых портов:

async def check_ports(host: str, start: int, end: int, max=10): found = 0

for port in range(start, end): try:

future = asyncio.open_connection(host=host, port=port) r, w = await asyncio.wait_for(future, timeout=timeout) yield port

found += 1 w.close()

if found >= max: return

except asyncio.TimeoutError: pass # Закрыт

Книги для программистов: https://t.me/booksforits

Субинтерпретаторы    255

Для выполнения кода используется команда async for:

async def scan(start, end, host): results = []

async for port in check_ports(host, start, end, max=1): results.append(port)

return results

Полный код примера находится в файле cpython-book-samples 33 portscanner_ async_generators.py.

СУБИНТЕРПРЕТАТОРЫ

К настоящему моменту мы рассмотрели:

zz Параллельное выполнение с многопроцессной обработкой. zz Конкурентное выполнение с потоками и async.

У многопроцессной обработки есть недостатки: межпроцессные коммуникации, использующие каналы и очереди, работают медленнее общей памяти, а дополнительные затраты на запуск нового процесса весьма значительны.

Дополнительные затраты у многопоточных решений и async невелики, но они не обеспечивают полноценного параллельного выполнения из-за гарантий потоковой безопасности в GIL.

Еще один вариант — создание субинтерпретаторов (subinterpreters); он имеет более низкие дополнительные затраты, чем у многопроцессных решений, и позволяет иметь отдельную блокировку GIL для каждого субинтерпретатора.

Вконце концов, GIL — глобальная блокировка интерпретатора.

Всреде выполнения CPython всегда существует только один интерпретатор. Он содержит информацию о состоянии, а внутри интерпретатора может быть один или несколько потоков Python. Интерпретатор является контейнером для цикла вычисления. Он также управляет своей памятью, обеспечивает подсчет ссылок и сборку мусора.

ВCPython поддерживаются низкоуровневые C API для создания интерпретаторов, например Py_NewInterpreter():

Книги для программистов: https://t.me/booksforits

256    Параллелизм и конкурентность

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

• •

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GIL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0

 

 

1 ... n

 

 

 

 

 

 

 

 

( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

• •

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GIL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0

 

 

1 ... n

 

 

 

 

 

 

 

 

( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Книги для программистов: https://t.me/booksforits

Субинтерпретаторы    257

ПРИМЕЧАНИЕ

Модуль subinterpreters остается экспериментальным вверсии 3.9,так что API еще может изменяться, а реализация содержит ошибки.

Так как состояние интерпретатора содержит арену выделения памяти — коллекцию всех указателей на объекты Python (локальные и глобальные), — субинтерпретаторы не могут обращаться к глобальным переменным других интерпретаторов.

Как и в случае с многопроцессной обработкой, для совместного использования объектов интерпретаторами необходимо сериализовать их или использовать ctype и некоторую разновидность IPC1 (сеть, диск, общая память).

Исходные файлы

Ниже перечислены исходные файлы, относящиеся к субинтерпретаторам.

ФАЙЛ НАЗНАЧЕНИЕ

Lib _xxsubinterpreters.c Реализация модуля subinterpreters на C

Python pylifecycle.c

Реализация API управления интерпретатором на C

Пример

В последнем примере код подключения будет храниться в строке. В версии 3.9 субинтерпретаторы могут выполняться только кодом в строке (string).

Для запуска каждого субинтерпретатора запускается список потоков с обратным вызовом функции run().

Эта функция:

zz создает канал взаимодействия;

zz запускает новый субинтерпретатор;

zz отправляет субинтерпретатору выполняемый код; zz получает данные по каналу взаимодействия;

1 Inter Process Communications; межпроцессное взаимодействие. — Примеч. ред.

Книги для программистов: https://t.me/booksforits

258    Параллелизм и конкурентность

zz если подключение к порту происходит успешно, он добавляется в потокобезопасную очередь.

cpython-book-samples 33 portscanner_subinterpreters.py

import time

import _xxsubinterpreters as subinterpreters from threading import Thread

import textwrap as tw from queue import Queue

timeout = 1 # В секундах

def run(host: str, port: int, results: Queue):

# Создание канала взаимодействия

channel_id = subinterpreters.channel_create() interpid = subinterpreters.create() subinterpreters.run_string(

interpid,

tw.dedent(

"""

import socket; import _xxsubinterpreters as subinterpreters sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout)

result = sock.connect_ex((host, port)) subinterpreters.channel_send(channel_id, result) sock.close()

"""),

shared=dict( channel_id=channel_id, host=host,

port=port,

timeout=timeout

))

output = subinterpreters.channel_recv(channel_id) subinterpreters.channel_release(channel_id)

if output == 0: results.put(port)

if __name__ == '__main__': start = time.time()

host = "127.0.0.1" # Выберите ваш хост threads = []

results = Queue()

for port in range(80, 100):

t = Thread(target=run, args=(host, port, results)) t.start()

threads.append(t) for t in threads:

Книги для программистов: https://t.me/booksforits