Лекция 3. Async, Threading, multiprocessing Flashcards

(77 cards)

1
Q

Что такое ядро?

A

— это основная часть операционной системы. Оно управляет аппаратным обеспечением и системными ресурсами.

Роль ядра: Ядро координирует доступ к процессору, памяти и другим устройствам.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Что такое процесс?

A

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

  • Изоляция: Процессы изолированы друг от друга, потоки — нет.
    Ресурсы: Процессы используют собственные ресурсы; потоки делят ресурсы процесса.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Что такое поток?

A

— это «легковесный процесс», который выполняется в рамках процесса. Потоки:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Что такое CPU-bound и I/O-bound операции?

A

CPU-bound задачи: интенсивно используют процессор. Пример – вычисление факториала большого числа.
I/O-bound задачи: больше зависят от операций ввода-вывода. Пример – загрузка данных с веб-сайта.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Что такое Global Interpreter Lock (GIL) в Python и как он влияет на многопоточность? Какие последствия могут возникнуть при использовании многопоточности в Python из-за GIL?

A

Это механизм в интерпретаторе CPython, который ограничивает выполнение Python-кода в один поток за раз, даже если в программе используется многопоточность. Это нужно для упрощения управления памятью, но из-за GIL многопоточные Python программы не могут эффективно использовать преимущества многоядерных процессоров для вычислительных задач.

**Последствия:**
- В многопоточных Python программах CPU-bound задачи могут страдать от пониженной производительности из-за GIL
- IO-bound задачи меньше страдают от GIL, так как операции ввода-вывода могут блокировать поток, освобождая GIL и позволяя другим потокам работатьЭто механизм в интерпретаторе CPython, который ограничивает выполнение Python-кода в один поток за раз, даже если в программе используется многопоточность
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Что такое многопоточность и какие преимущества она может предоставить при разработке программного обеспечения?

A

Это вариант реализации вычислений, при котором выполнение некоторой прикладной задачи запускается и выполняется в нескольких потоках.

**Преимущества:**
- Удобство использования для I/O-интенсивных задач и параллельной обработки
- Отзывчивость приложений при обработке событий, таких как пользовательский ввод или сетевые запросы
- Более низкие затраты на создание и управление потоками по сравнению с процессами
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Что такое асинхронное программирование и какие преимущества оно может предоставить при разработке программного обеспечения?

A

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

**Преимущества:**
- Высокая производительность I/O-bound задач
- Эффективное использование ресурсов
- Отзывчивость интерфейса/сервиса
- Масштабируемость
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

В чем разница между многопоточностью и асинхронностью? Какой подход лучше использовать в каких ситуациях?

A

Многопоточность использует несколько потоков ОС для параллельного выполнения задач в одном процессе, обходя блокировки ввода, вывода (I/O-bound).

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

Основные различия:

  • Исполнение: Многопоточность создает реальные потоки ОС, а асинхронность – кооперативная модель, где задачи сами передают управление
  • Ресурсы: Потоки потребляют больше памяти, асинхронность экономичнее
  • Асинхронность лучше для высоконагруженных IO-bound задач
  • Многопоточность подходит для задач, работающих с библиотеками, не поддерживающими async, или при необходимости удобного управления параллельными задачами
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Как создать поток в Python? Какие способы существуют для создания потоков?

A

Потоки (threads) в Python создаются с помощью стандартного модуля threading, предназначенного для параллельного выполнения задач, связынных с вводом-выводом. Основные способы включают передачу целевой функции в конструктор threading.Thread(target=...) или создание подкласса от Thread с переопределением метода run(). Запуск потока осуществляется методом .start(), а ожидание его завершения – .join().

```python
def my_function():
print(“Поток работает”)
time.sleep(1)

Создание потока
thread = threading.Thread(target=my_function)

Запуск потока
thread.start()

Ожидание завершения потока
thread.join()
print(“Поток завершен”)
~~~

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Какие модули Python вы знаете для работы с многопоточностью?

A

Встроенные модули

	- `threading`Основной высокоуровневый модуль для создания и управления потоками
	
	- `concurrent.futures` Выскоуровневый интерфейс для асинхронного выполнения вызовов
	
	- `queue` Модуль для создания безопасных потоковых очередей (FIFO). Используется для обмена данными между потоками, чтобы избежать состояния гонки
	
	- `_thread` Низкоуровневый модуль. В основном используется `threading`, так как он удобнее и безопаснее
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Какие модули Python вы знаете для работы с асинхронностью?

A

Основные модули и библиотеки, разделенные по назначению

  1. Стандартная библиотека
    - asyncio Основной модуль в стандартной библиотеке Python для написания однопоточного конкурентного кода с использованием async/await
    - concurrent.futures Высокоуровневый интерфейс для асинхронного выполнения вызываемых объектов
  2. Асинхронные HTTP-клиенты и сервера
    - aiohttp Самая популярная библиотека для создания асинхронных HTTP-клиентов и серверов
    - httpx Современный клиент, который поддерживает как синхронный, так и асинхронный режимы, совместимый с API requests
    - websockets Библиотека для работы с WebSocket-соединениями
  3. Асинхронные веб-фреймворки
    - FastAPI. Высокопроизводительный фреймворк для создания API
    - Starlette Легковесный ASGI-фреймворк, идеальный для высокопроизводительных сервисов
    - Sanic Фреймворк, ориентированный на высокую скорость работы
    - Quart Микрофреймворк, совместимый с API Flask, но работающий асинхронно
  4. Асинхронные драйверы баз данных (ORM)
    - asyncpg
    - motor
    - aiomysql / aiopg
    - GINO
    - Beanie
  5. Альтернативные библиотеки/экосистемы
    - trio
    - anyio
    - gevent
  6. Ускорение
    - uvloop
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Что такое event loop (цикл событий) в асинхронном программировании? Как он работает в Python?

A

Это реализация паттерна Реактор в асинхронном программировании, которая позволяет системе обрабатывать большое количество задач без создания множества потоков

Основные шаги работы event loop:

1. Ожидание событий. Цикл событий ожидает появления новых задач
2. Помещение задачи в очередь. Когда событие происходит, оно добавляется в очередь задач, ожидающих обработки
3. Выполнение задачи. Цикл событий начинает обработку задачи, извлекая ее из очереди и вызывая соответствующий обработчик
4. Завершение задачи. Когда задача завершена, цикл переходит к следующему событию в очереди
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Какие проблемы могут возникнуть при использовании асинхронности? Как их можно решить?

A
  1. Блокировка событийного цикла
    Использование синхронных библиотек или тяжелых вычислений внутри async def функции блокирует весь поток выполнения
  2. Забытый wait
    Вызов асинхронной функции без ключевого слова await
  3. CPU-bound задачи (тяжелые вычисления)
    Если нужно обрабатывать видео, тяжелые JSON или заниматься машинным обучением, asyncio не поможет, а лишь добавит накладные расходы
  4. Сложность обработки ошибок
    Если одна задача внутри asyncio.gather() вызывает исключение, другие задачи могут остаться висеть, а само исключение может “проглотиться” и не быть замеченным
  5. “Зависшие” задачи (Dangling Tasks)
    Задачи созданы через asyncio.create_task(), но не дождались завершения (await), из-за чего могут зависнуть при выходе из программы
  6. Неправильное управление таймаутами
    Асинхронный запрос к внешнему API может зависнуть навсегда, заблокировав логику
  7. Сложности отладки
    Асинхронный код сложнее читать, и он имеет нелинейный стек вызовов, что затрудняет классическую отладку
  8. Race Conditions (Состояние гонки)
    Несколько асинхронных задач пытаются изменить одну и ту же переменную или файл одновременно
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

GIL’s problem. Если lock - это mutex, то почему же возможен результат отличный от 0?

A

GIL гарантирует, что в один момент времени исполняется только один байткод Python, но он не делает операции над данными атомарными.
Mutex (lock) защищает только тот участок кода, который обернут. Если критическая секция выбрана неправильно, используется несколько локов или часть операций выполняется без блокировки, возможны race condition и результат, отличный от ожидаемого.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

При использовании TaskGroup в случае исключения другие задачи могут быть отменены или автоматически отменяются?

A

Да, Если любая задача внутри TaskGroup выбрасывает исключение, TaskGroup автоматически отменяет все остальные еще не завершенные задачи.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Какие способы синхронизации потоков вы знаете в Python?

A
  • Блокировки (Lock)
  • Замок для чтения/записи (Rlock)
  • Семафоры (Semaphore)
  • Условные переменные (Condition)
  • События (Event)
  • Барьеры (Barrier)
  • Мьютекс (mutex)
  • Очередь (queue.Queue)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Почему свитчинг контекста между потоками дороже чем между корутинами? User space и kernel space?

A

Переключение контекста между потоками операционной системы дороже, чем между корутинами, в основном из-за того, что потоки управляются ядром, а корутины – приложением. Переключение потоков требует участия операционной системы, в то время как корутины переключаются внутри самого процесса

User Space (Пользовательское пространство): Это непривилегированный режим работы процессора (Ring 3 в архитектуре x86). Программы здесь имеют доступ только к своей виртуальной памяти и не могут напрямую управлять железом.

Kernel Space (Пространство ядра): Это привилегированный режим работы (Ring 0). Здесь работает ядро ОС, имеющее полный контроль над памятью, прерываниями и периферией.

Context Switch (Переключение контекста): Процесс сохранения состояния (регистров, стека) одной задачи и восстановления состояния другой.

Mode Switch (Переключение режима): Переход процессора из Ring 3 в Ring 0 (и обратно) через системный вызов или прерывание.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Manager vs Lock (multiprocessing)

A

Manager предоставляют доступ к разделителям, объектам и ресурсам, таким как разделяемые списки, словари и прокси-объекты. Это позволяет разным процессам взаимодействовать с общими данными. Один из наиболее частых вариантов использования менеджера – работа с разделяемыми контейнерами данных, такими как разделяемые списки или словари.

Lock используются для обеспечения синхронизации доступа к разделяемым ресурсам между несколькими процессами. Они позволяют предотвратить гонки данных и обеспечить правильный и согласованный доступ к разделяемым ресурсам.

В реальных задачах Manager часто используют вместе с Lock, а для высоконагруженных сценариев выбирают shared memory или Value / Array

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Основные варианты создания фоновых задач:

A

Фоновые задачи в Python можно реализовывать через потоки, процессы, asyncio, executors, а для надежных и долгих задач – через очереди задач вроде Celery. Выбор зависит от того, IO или CPU-bound задача, и нужны ли ретраи и масштабирование

  1. Потоки
  2. Процессы
  3. asyncio
  4. ThreadPoolExecutor / ProcessPoolExecutor
    Когда фоновые задачи из sync-кода
  5. Очереди задач (Celery, RQ, Dramatiq)
    Когда долгие, надежные фоновые задачи
  6. Планировщики задач
    - cron
    - APSheduler
    - Celery Beat
  7. ОС-уровень
    Когда отдельный сервис
    - systemd service
    - Docker container
    - Kubernetes Job / CronJob
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Основные причины существования GIL

A

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

Поддержание интерпретатора Python:
- GIL упрощает интерпретатор Python, так как многие внутренние структуры данных CPython могут быть не потокобезопасными. Это позволяет разработчикам Python сосредоточиться на производительности и функциональности самого языка, не тратя ресурсы на сложные механизмы синхронизации.

Предотвращение гонок данных:
- Python является динамически типизированным языком, и объекты могут менять свой тип и состояние во время выполнения программы. Без GIL множество потоков могло бы одновременно изменять и обращаться к общим объектам, что могло бы привести к гонкам данных и непредсказуемому поведению программы.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Последствия и ограничения GIL

A

Многопоточность и производительность:
- В многопоточных Python программах CPU-bound задачи (которые интенсивно используют процессор) могут страдать от пониженной производительности из-за GIL. В таких программах только один поток может исполняться в любой момент времени, что ограничивает параллельное выполнение на многоядерных системах.

  • IO-bound задачи (которые часто выполняют операции ввода-вывода) меньше страдают от GIL, так как операции ввода-вывода могут блокировать поток, освобождая GIL и позволяя другим потокам работать.

Альтернативы и обходные пути:
- Для задач, требующих высокой производительности и параллельного выполнения, можно использовать мультипроцессинг вместо многопоточности. Модуль multiprocessing создает отдельные процессы, каждый из которых имеет свой собственный GIL, что позволяет эффективно использовать многоядерные системы.

  • Также существуют альтернативные реализации Python без GIL, такие как Jython и IronPython, которые убирают это ограничение, но имеют свои собственные особенности и ограничения.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Плюсы и минусы GIL

A

Преимущества GIL в Python:

  1. Упрощенное управление памятью: GIL позволяет интерпретатору CPython управлять памятью более безопасно. Это облегчает работу с автоматическим управлением памятью и уменьшает риск утечек памяти.
  2. Простота реализации и отладки: GIL упрощает разработку и отладку интерпретатора Python, так как он устраняет многие сложности, связанные с параллельным выполнением кода
  3. Совместимость с C-расширениями: GIL обеспечивает совместимость с существующими C-расширениями для Python, которые могут не быть потокобезопасными.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Недостатки GIL в Python:

A
  1. Ограничение параллелизма: Основной недостаток GIL заключается в том, что он ограничивает параллельное выполнение Python-кода только одним потоком в один момент времени. Это делает многозадачное многопоточное программирование на Python менее эффективным на многопроцессорных системах.
  2. Низкая многозадачность: Из-за GIL многозадачные приложения на Python могут столкнуться с проблемами производительности, особенно когда требуется высокая степень параллелизма, например, в приложениях, связанных с параллельной обработкой данных или сетевыми запросами.
  3. Сложности в многозадачных приложениях: В многозадачных приложениях, особенно тех, которые требуют совместного доступа к общим ресурсам, необходимо аккуратное управление синхронизацией и блокировками, чтобы избежать гонок данных.
  4. Снижение производительности в некоторых случаях: В некоторых случаях, например, при выполнении CPU-интенсивных вычислений, GIL может снижать производительность приложения, так как только один поток может выполнять код Python в данное время.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Что такое многопоточность?

A

Это вариант реализации вычислений, при котором выполнение некоторой прикладной задачи запускается и выполняется в нескольких потоках.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Что такое мультипроцессинг?
**Мультипроцессинг**: Каждый процесс имеет свой GIL и независимое адресное пространство, что позволяет эффективно использовать все ядра процессора. Это значительно ускоряет выполнение.
26
Зачем нужна многопоточность?
1. ** Конкурентное выполнение задач:** Многозадачность позволяет выполнять несколько задач (или операций) псевдо-одновременно, что может улучшить производительность программы в многозадачных сценариях. 2. **Отзывчивость приложений:** Многозадачные приложения могут оставаться отзывчивыми при выполнении задач в фоновом режиме, таких как обработка пользовательского ввода или сетевых запросов, без блокировки основного потока выполнения.
27
Что такое Блокировки (Lock)?
`asyncio.Lock` обеспечивает мьютекс (взаимное исключение) для асинхронных задач, гарантируя, что только одна задача может захватить лок в любой момент времени. **Пример использования:** ```python import asyncio async def critical_section(lock, task_id): async with lock: print(f'Task {task_id} entered critical section') await asyncio.sleep(1) print(f'Task {task_id} exited critical section') async def main(): lock = asyncio.Lock() tasks = [critical_section(lock, i) for i in range(5)] await asyncio.gather(*tasks) asyncio.run(main()) ```
28
Что такое замок для чтения/записи (RLock)?
RLock (от англ. Reentrant Lock) — это «повторно входимая» блокировка, которая позволяет одному и тому же потоку захватывать один и тот же замок несколько раз, не блокируя самого себя
29
Что такое семафоры (Semaphore)?
`asyncio.Semaphore` ограничивает количество одновременных доступов к ресурсу. Он полезен, когда нужно контролировать доступ к ограниченному количеству ресурсов, таких как соединения с базой данных или запросы к API. **Пример использования:** ```python import asyncio async def access_resource(semaphore, resource_id): async with semaphore: print(f'Resource {resource_id} is being accessed') await asyncio.sleep(1) >> print(f'Resource {resource_id} is released') async def main(): semaphore = asyncio.Semaphore(2) tasks = [access_resource(semaphore, i) for i in range(5)] await asyncio.gather(*tasks) asyncio.run(main()) ```
30
Что такое условные переменные (Condition)?
`asyncio.Condition` сочетает в себе функциональность `asyncio.Lock` и возможности уведомления, как в `asyncio.Event`. Он позволяет задачам ожидать определенных условий и уведомлять другие задачи, когда условия выполняются. **Пример использования:** ```python import asyncio async def consumer(condition, task_id): async with condition: print(f'Task {task_id} waiting for condition') await condition.wait() print(f'Task {task_id} condition met') async def producer(condition): await asyncio.sleep(2) async with condition: print('Notifying all consumers') condition.notify_all() async def main(): condition = asyncio.Condition() tasks = [consumer(condition, i) for i in range(3)] tasks.append(producer(condition)) await asyncio.gather(*tasks) asyncio.run(main()) ```
31
Что такое события (Event)?
`asyncio.Event` используется для уведомления нескольких задач о наступлении какого-либо события. Событие может быть установлено (set) или сброшено (clear). **Пример использования:** ```python import asyncio async def waiter(event, task_id): print(f'Task {task_id} waiting for event') await event.wait() print(f'Task {task_id} detected event') async def main(): event = asyncio.Event() tasks = [waiter(event, i) for i in range(3)] await asyncio.sleep(2) await event.set() await asyncio.gather(*tasks) asyncio.run(main()) ```
32
Что такое барьеры (Barrier)?
Barrier — это примитив синхронизации, который заставляет группу корутин ждать, пока они все не достигнут определенной точки. Как это работает в asyncio.Barrier? При создании барьера вы указываете число корутин (parties), которые он должен собрать. 1. Каждая корутина вызывает await barrier.wait(). 2. Корутина «засыпает» на этой строчке, если нужное количество участников еще не набралось. 3. Как только wait() вызовет последняя (N-ая) корутина, все ожидающие одновременно просыпаются и продолжают выполнение.
33
Что такое мьютекс (mutex)?
Mutual exclusion — взаимное исключение — это простейший примитив синхронизации, который гарантирует, что только один поток (или корутина) имеет доступ к общему ресурсу в конкретный момент времени
34
Что такое очередь (queue.Queue)?
Это структура данных, которая позволяет безопасно передавать сообщения или объекты между корутинами. Обычно её используют для реализации паттерна «Producer-Consumer» (Производитель — Потребитель), где одна группа корутин генерирует задачи, а другая их выполняет. Ключевые фишки asyncio.Queue 1. Асинхронное ожидание: В отличие от обычного queue.Queue из многопоточности, методы get() и put() здесь являются корутинами. Если очередь пуста, await queue.get() не «вешает» весь процесс, а приостанавливает только одну корутину, давая другим работать. 2. Управление давлением (Backpressure): Вы можете ограничить размер очереди (maxsize). Если очередь заполнится, «производители» встанут на паузу (await put()), пока в очереди не освободится место.
35
Плюсы и минусы многозадачности
**Плюсы многозадачности:** - Удобство использования для I/O-интенсивных задач и параллельной обработки. - Отзывчивость приложений при обработке событий, таких как пользовательский ввод или сетевые запросы. - Более низкие затраты на создание и управление потоками по сравнению с процессами. **Минусы многозадачности:** - Ограничен GIL в CPython, что может снижать производительность в CPU-интенсивных задачах. [*в каждый момент времени выполняется только одна инструкция питонского байт-кода*] - Переключение контекста между потоками - **дорогостоящая операция!** - Больше сложностей в управлении синхронизацией и избегании гонок данных между потоками. - Не подходит для задач, которые требуют изоляции данных между потоками.
36
Что такое garbage collector?
Garbage Collector (сборщик мусора) в Python отвечает за автоматическое управление памятью, обеспечивая освобождение памяти, которая больше не используется программой. Python использует счетчик ссылок и сборщик мусора для обнаружения и очистки неиспользуемых объектов.
37
Что такое счетчик ссылок?
Счетчик ссылок — это механизм, который отслеживает количество активных ссылок на объект в памяти. Каждый объект в Python имеет счетчик ссылок, который увеличивается или уменьшается, когда ссылки на этот объект создаются или удаляются соответственно.
38
Как работает счетчик ссылок?
1. **Создание объекта**: Когда объект создается, его счетчик ссылок устанавливается в 1. 2. **Создание ссылки**: Когда новая ссылка указывает на объект, его счетчик ссылок увеличивается на 1. 3. **Удаление ссылки**: Когда ссылка на объект удаляется или меняется, его счетчик ссылок уменьшается на 1. 4. **Освобождение памяти**: Когда счетчик ссылок объекта достигает 0, объект становится недоступным, и память, занимаемая этим объектом, **может быть** освобождена.
39
Что такое циклические ссылки?
Циклические ссылки возникают, когда объекты ссылаются друг на друга, создавая цикл. В таких случаях счетчик ссылок не может освободить объекты, так как счетчик ссылок объектов в цикле никогда не достигает 0. Из-за этого счетчик ссылок эффективно управляет памятью для большинства объектов, однако у него есть ограничения, особенно при работе с циклическими ссылками.
40
Что используется дл решения проблемы циклических ссылок?
**Дополнительный механизм сборки мусора** Для решения проблемы циклических ссылок Python использует дополнительный механизм сборки мусора на основе **алгоритма подсчета поколений** (generational garbage collection). Этот механизм включает в себя три поколения объектов: 1. **Поколение 0** (новые объекты): Содержит недавно созданные объекты. Сборка мусора запускается часто. 2. **Поколение 1** (средний возраст объектов): Объекты, пережившие сборку мусора в поколении 0. 3. **Поколение 2** (старые объекты): Объекты, пережившие несколько сборок мусора. Алгоритм основан на предположении, что большинство объектов живет недолго, поэтому новая память проверяется чаще, чем старая.
41
Как происходит переход между поколениями?
**Переход между поколениями** 1. **Из поколения 0 в поколение 1**: Когда количество операций выделения памяти минус количество операций освобождения памяти достигает определенного порога, происходит сборка мусора для объектов поколения 0. Все объекты, пережившие эту сборку мусора (те, которые не были освобождены), перемещаются в поколение 1. 2. **Из поколения 1 в поколение 2**: Подобно переходу из поколения 0, когда объекты поколения 1 переживают сборку мусора и остаются "в живых", они перемещаются в поколение 2. Пороги для сборки мусора в поколении 1 обычно выше, чем в поколении 0, чтобы избежать частых сборок мусора на "старших" уровнях. 3. **В поколении 2**: Это поколение является конечной станцией для объектов. Сборка мусора в поколении 2 происходит реже, и объекты, пережившие её, остаются в этом поколении. Сборка мусора в поколении 2 включает в себя также и объекты поколений 1 и 0.
42
Как настроить сборщик мусора?
Вы можете контролировать процесс сборки мусора с помощью модуля gc в Python. С его помощью можно вручную запускать сборку мусора, изменять пороги сборки мусора для различных поколений, отключать и включать сборщик мусора, и так далее. `Стоит помнить, что чрезмерная настройка и частые сборки мусора могут негативно повлиять на производительность вашего приложения, так как сборка мусора — это ресурсоёмкая операция. В большинстве случаев, стандартные параметры сборщика мусора Python оптимизированы для общих сценариев использования и не требуют значительных изменений.`
43
Что такое Asyncio?
asyncio — это библиотека в Python, используемая для написания асинхронных программ с использованием синтаксиса async/await. Она предоставляет средства для параллелизации кода через корутины, интерфейс для создания событийных циклов, а также обеспечивает поддержку для асинхронного ввода-вывода (I/O), интерпроцессного взаимодействия и управления подпроцессами.
44
Основная цель Asyncio?
Основная цель asyncio — создание и управление асинхронностью и событийными циклами в Python, что особенно полезно в случаях, когда вам нужно обработать множество I/O-операций, как, например, сетевые запросы, без необходимости использования многопоточности или многопроцессорности.
45
Что такое корутина?
**Корутина** (coroutine или сопрограмма) - это функция, возвращающая объект корутины, отмеченная ключевым словом async. Она является нативной корутиной или асинхронным генератором. Это функция, которая будет выполняться с помощью asyncio.
46
Что такое await?
**Await** - ключевое слово для остановки выполнения текущей корутины и переключения контекста выполнения евент лупа на следующую корутину. Контекст будет возвращен обратно и выполнение продолжится с того же самого места, когда появится результат выражения, отмеченного ключевым словом **await.**
47
Что такое task?
**Task** - специальный объект для шедулинга корутин. После создания таски можно ее отменить, вызвав метод cancel() или дождаться выполнения и результата, просто вызвав await:
48
Что такое futute?
Future - cпециальный low-level объект, родитель класса Task, для предоставления конечного результата асинхронной операции. Когда футура авейтится - это означает, что текущая корутина будет ожидать, когда футура зарезолвится в каком то другом месте.
49
Что такое событийный цикл?
**Событийный цикл (Event Loop)** — это основа асинхронного программирования в `asyncio`, который управляет выполнением различных задач: переключается между задачами в соответствии с их статусом готовности и ожидания.
50
Чем отличается future и coroutine?
`Future` — это результат асинхронной операции, который будет получен в будущем, а **`coroutine`** — это функция или генератор, который может быть приостановлен и возобновлен для асинхронного выполнения. **Основные отличия** 1. **Уровень абстракции**: - - **Корутина**: Более высокоуровневое понятие. Корутины используются для написания асинхронных функций, которые могут выполняться параллельно. - - **Фьючерс**: Более низкоуровневое понятие. Фьючерсы используются для представления и управления результатами асинхронных операций. 2. **Назначение**: - **Корутина**: Содержит логику асинхронной операции, которая может приостанавливаться и возобновляться. Корутины используют ключевые слова `async` и `await`. - **Фьючерс**: Представляет собой контейнер для результата асинхронной операции, который станет доступен в будущем. Фьючерсы используются для работы с результатами корутин или других асинхронных операций. 3. **Взаимодействие**: - Корутины могут использовать фьючерсы для ожидания результата асинхронной операции. Например, корутина может `await` фьючерс для получения его результата. 4. **Создание и запуск**: - **Корутина**: Создается с использованием ключевых слов `async def` и `await`. Запускается через `asyncio.run()` или `asyncio.create_task()`. - **Фьючерс**: Создается с использованием `asyncio.Future()` или `loop.create_future()`. Результат фьючерса устанавливается через методы `set_result()` или `set_exception()`.
51
Принцип работы await
**Принцип работы `await`** Когда корутина ожидает завершения **`Future`** через **`await`**, следующее происходит: 1. **Приостановка выполнения корутины**: Когда исполнение достигает выражения **`await`**, корутина приостанавливается. Это не блокирует весь поток исполнения, как это происходит с синхронным кодом, поскольку использование **`await`** предполагает, что у вас есть асинхронный событийный цикл, который может продолжить выполнять другие задачи. 2. **Передача управления событийному циклу**: Когда корутина приостанавливается, управление передается обратно в событийный цикл (event loop), который может затем запустить выполнение другой корутины или продолжить выполнять другие типы задач. 3. **Ожидание завершения Future**: Событийный цикл продолжает выполняться, пока асинхронная операция (или операции), которую ожидает корутина, не завершится и не установит значение в соответствующем объекте **`Future`**. 4. **Возобновление корутины**: Как только **`Future`** завершится (т.е. у него установится результат), корутина возобновляется с того места, где она была приостановлена, и значение, установленное в **`Future`**, передается обратно в корутину.
52
asyncio.gather
Это функция в Python, которая предоставляет возможность запускать несколько корутин асинхронно и параллельно конкурентно. Эта функция часто используется для того, чтобы "собирать" результаты из нескольких асинхронных операций, выполняемых одновременно. **Основные моменты использования `asyncio.gather`:** - **Конкурентное выполнение**: Она позволяет вам запускать несколько корутин псевдо-параллельно(конкурентно) и дожидаться их завершения. - **Сбор результатов**: Возвращает результаты выполнения всех переданных корутин в порядке, в котором они были переданы.
53
asyncio.Lock
asyncio.Lock > `asyncio.Lock` обеспечивает мьютекс (взаимное исключение) для асинхронных задач, гарантируя, что только одна задача может захватить лок в любой момент времени. > **Пример использования:** ```python import asyncio async def critical_section(lock, task_id): async with lock: print(f'Task {task_id} entered critical section') await asyncio.sleep(1) print(f'Task {task_id} exited critical section') async def main(): lock = asyncio.Lock() tasks = [critical_section(lock, i) for i in range(5)] await asyncio.gather(*tasks) asyncio.run(main()) ```
54
asyncio.Event
**asyncio.Event** `asyncio.Event` используется для уведомления нескольких задач о наступлении какого-либо события. Событие может быть установлено (set) или сброшено (clear). **Пример использования:** ```python import asyncio async def waiter(event, task_id): print(f'Task {task_id} waiting for event') await event.wait() print(f'Task {task_id} detected event') async def main(): event = asyncio.Event() tasks = [waiter(event, i) for i in range(3)] await asyncio.sleep(2) await event.set() await asyncio.gather(*tasks) asyncio.run(main()) ```
55
asyncio.Condition
**asyncio.Condition** `asyncio.Condition` сочетает в себе функциональность `asyncio.Lock` и возможности уведомления, как в `asyncio.Event`. Он позволяет задачам ожидать определенных условий и уведомлять другие задачи, когда условия выполняются. **Пример использования:** ```python import asyncio async def consumer(condition, task_id): async with condition: print(f'Task {task_id} waiting for condition') await condition.wait() print(f'Task {task_id} condition met') async def producer(condition): await asyncio.sleep(2) async with condition: print('Notifying all consumers') condition.notify_all() async def main(): condition = asyncio.Condition() tasks = [consumer(condition, i) for i in range(3)] tasks.append(producer(condition)) await asyncio.gather(*tasks) asyncio.run(main()) ```
56
asyncio.gather
Это функция в Python, которая предоставляет возможность запускать несколько корутин асинхронно и параллельно конкурентно. Эта функция часто используется для того, чтобы "собирать" результаты из нескольких асинхронных операций, выполняемых одновременно.
57
58
Обработка ошибок с asyncio.gather
asyncio.gather позволяет вам эффективно обрабатывать исключения, бросаемые внутри корутин. Вы можете использовать аргумент **return_exceptions** для того, чтобы получить все результаты, несмотря на то, возникли ли исключения или нет. Даже если какая-то задача выбросит исключение, asyncio.gather продолжит выполнение остальных задач и вернет результаты всех корутин (включая исключения) в виде списка.
59
Варианты использования asyncio.gather
1. **Параллельные API-вызовы**: Если у вас есть несколько асинхронных API-вызовов или IO-операций, которые могут быть выполнены параллельно, используйте `asyncio.gather` для одновременного запуска и получения всех результатов сразу. 2. **Массовая обработка данных**: Если вам нужно асинхронно обработать большие массивы данных (например, записи в базе данных или файлы), вы можете использовать `asyncio.gather` для параллельной обработки и увеличения производительности. 3. **Веб-скрапинг**: Когда вы извлекаете данные с нескольких веб-страниц, `asyncio.gather` может параллельно запустить несколько асинхронных задач скрапинга для увеличения скорости загрузки.
60
Как работает TaskGroup?
Это современный и безопасный способ запуска группы корутин. Это реализация концепции Structured Concurrency (структурного параллелизма). TaskGroup используется как асинхронный контекстный менеджер (async with). Его главная задача — гарантировать, что все запущенные задачи будут завершены (успешно или с ошибкой) до выхода из блока async with.
61
Особенности TaskGroup
1. **Контекстный менеджер:** - `TaskGroup` используется внутри блока `async with`, что позволяет автоматически управлять созданием и завершением задач. 2. **Отслеживание и отмена задач:** - Если одна из задач в группе вызывает исключение, все другие задачи в группе автоматически отменяются. 2. **Иерархия задач:** - Поддерживает создание вложенных групп задач, что позволяет более структурированно организовывать асинхронный код. 3. **Обработка исключений:** - Исключения, возникающие в задачах, могут быть пойманы и обработаны с помощью блока `try-except`.
62
Преимущества использования TaskGroup
1. **Управление задачами:** Тасковые группы позволяют запускать, отменять и отслеживать завершение всех задач в группе. Если одна из задач вызывает исключение, другие задачи могут быть отменены. 2. **Автоматическое завершение:** Когда блок `async with` завершается, тасковая группа ждет завершения всех задач и автоматически завершает их выполнение. 3. **Упрощение кода:** Тасковые группы упрощают написание асинхронного кода, делая его более читабельным и управляемым.
63
Как устанавливается результат в Future?
Результат (или исключение) для объекта `Future` может быть установлен в следующих ситуациях: 1. **Асинхронные операции**: В контексте асинхронной операции, например, сетевого запроса, объект `Future` может быть использован для хранения результата операции. Когда асинхронная операция завершается, результат (или исключение в случае ошибки) устанавливается в `Future` через его методы `.set_result(result)` или `.set_exception(exception)`. 2. **Коллбеки и Future**: В некоторых случаях, например, при работе с библиотеками, поддерживающими асинхронный ввод/вывод, результат может быть установлен в `Future` с использованием коллбеков. Когда асинхронная операция завершается, коллбек вызывается, и он, в свою очередь, устанавливает результат в `Future`. 3. **Ручное управление**: В некоторых сценариях, разработчик может вручную управлять объектами `Future`, устанавливая результаты или исключения вручную для контроля над потоком выполнения асинхронной программы
64
Timeout Management (Управление тайм-аутами)
Управление тайм-аутами в `asyncio` важно для обеспечения того, чтобы асинхронные задачи не занимали слишком много времени. Это помогает предотвратить блокировку программы из-за долгих или зависших операций. `asyncio.wait_for` ожидает завершения корутины в течение заданного времени. Если корутина не завершится в указанный период, будет вызвано исключение `asyncio.TimeoutError`. **Что используется под капотом** `asyncio.wait_for` создает задачу (`asyncio.Task`) для выполнения корутины и отслеживает её выполнение с помощью таймера. Если задача не завершается до истечения времени тайм-аута, она будет отменена.
65
Отмена задач (Cancel)
При истечении тайм-аута asyncio.wait_for вызывает метод cancel для задачи. Это посылает задаче запрос на отмену, который она может обработать при следующей точке ожидания (например, при await или yield).
66
Обработка отмены дочерних задач
При отмене задачи, любые дочерние задачи, которые она запустила, также могут быть отменены, если основной задачей был передан сигнал отмены. Это достигается передачей исключения `asyncio.CancelledError` через иерархию задач.
67
Threading vs Multiprocessing. I/O bound vs CPU bound операции
Потоки в Python подходят для IO-bound задач, потому что во время ожидания IO они освобождают GIL. Для CPU-bound задач нужен multiprocessing, так как каждый процесс имеет свой GIL и может использовать отдельное ядро процессора
68
Asyncio vs Threading
Они похожи тем, что оба используются для IO-bound задач, но принципиально отличаются моделью выполнения. В threading переключиением управляет операционная система, поэтому возможны гонки данных и нужны локи. В asyncio используется кооперативная многозадачность – переключение происходит только в точках await, что делает выполнение более предсказуемым и масштабируемым
69
Концепция работы event loop и как происходит взаимодействие с дескрипторами системы (epoll, select)
Концептуально, Event Loop также очень прост: каждый раз в цикле он вызывает любой обратный вызов из списка "готовых" обратных вызовов, а затем использует селектор для ожидания завершения следующей запланированной задачи, после чего он добавляет обратный вызов, который будет обрабатывать событие, в список `call_soon` и начинает новую итерацию цикла `epoll` используется для эффективного управления операциями ввода-вывода. Вот как это работает: 1. При выполнении операций ввода-вывода asyncio регистрирует дескриптор файла в селекте 2. Селектор отслеживает несколько файловых дескрипторов одновременно 3. Когда ввод-вывод готов, селектор уведомляет цикл событий 4. Цикл событий возобновляет работу соответствующей сопрограммы
70
TaskGroup vs Gather в чем отличие?
Gather - Если задача падает, остальные продолжают работать - Обычный вызов функции: `await gather(coro1, coro2)` - Возвращает список результатов в порядке передачи задач - Возвращает первое возникшее исключение TaskGroup - Если одна задача падает, все остальные задачи в группе автоматически отменяются - Асинхронный контекстный менеджер: `async with TaskGroup() as tg` - Результаты нужно собирать вручную из объектов задач через `.result()` после выхода из блока - Выбрасывает `ExceptionGroup`, объединяющий ошибки всех упавших задач
71
Обработка exception в TaskGroup, Gather
Gather - gather возвращает ошибку, остальные задачи продолжают работать в фоновом режиме - вы увидите только первое исключение. Остальные будут проигнорированы или вызовут предупреждение - Нет гарантии, что после `await gather` все реально остановилось TaskGroup - Все остальные задачи в группе принудительно отменяютмя - Вы получите `ExceptionGroup`, содержащую все возникшие ошибки - Гарантирует, что при выходе из блока `async with` ни одна задача из группы больше не выполняется
72
Queue - Очередь (multiprocessing)
Очереди можно использовать для организации обмена данными между процессами. Они обеспечивают безопасный доступ к данным и синхронизацию между процессами.
73
Pipe - Конвейер (multiprocessing)
Конвейеры предоставляют двусторонний канал для обмена данными между процессами. Они могут быть использованы для более сложных сценариев взаимодействия. Каждый конец канала можно использовать как файлоподобный объект для отправки и приема данных.
74
Manager – Менеджер (multiprocessing)
Менеджеры предоставляют доступ к разделяемым объектам и ресурсам, таким как разделяемые списки, словари и прокси-объекты. Это позволяет разным процессам взаимодействовать с общими данными. Один из наиболее частых вариантов использования менеджера - работа с разделяемыми контейнерами данных, такими как разделяемые списки или словари.
75
Event – Событие (multiprocessing)
События используются для синхронизации между процессами. Они позволяют процессам ждать наступления определенных событий перед продолжением выполнения.
76
Lock – Блокировки (multiprocessing)
Блокировки (Lock) в модуле multiprocessing используются для обеспечения синхронизации доступа к разделяемым ресурсам между несколькими процессами. Они позволяют предотвратить гонки данных и обеспечить правильный и согласованный доступ к разделяемым ресурсам.
77