Итак, в прошлом видео мы обсудили процессы.
В этом видео мы поговорим о потоках.
Мы обсудим создание потоков при помощи модуля threading
и использование класса ThreadPoolExecutor.
С прикладной точки зрения поток
целиком и полностью напоминает процесс.
Он имеет свою последовательность инструкций для исполнения,
у каждого потока есть свой собственный стек,
но все потоки выполняются в рамках одного процесса.
Этим они отличаются.
Если мы говорили о процессах и у каждого процесса
были свои ресурсы и память, то все созданные потоки
разделяют память процесса и все его ресурсы.
Управлением и выполнением потоков занимается операционная система.
Но в Python есть свои ограничения для потоков.
Их мы обсудим отдельно.
Итак, давайте рассмотрим пример, как создать поток на Python.
Всё делается очень просто и похоже на создание процессов.
Используем модуль threading, импортируем класс Thread.
Далее мы объявляем функцию,
которую хотим исполнить в отдельном потоке
функции f, которая просто выводит hello
и аргумент, который ей передали.
Далее мы создаем объект класса Thread, передаем в него
нашу функцию f в качестве параметров и аргументы,
с которыми эта функция должна будет быть вызвана.
Опять же, после того как мы создали объект,
никакого потока запущено не будет.
Поток будет запущен, после того как мы вызовем
метод start у этого объекта.
Также очень важно дожидаться выполнения завершения
всех созданных потоков при помощи метода join у этого объекта.
Если мы выполним пример в консоли с этого слайда,
то получим вывод hello Bob.
Существует также альтернативный метод
создания потока при помощи наследования.
Опять же, всё очень похоже на использование модуля multiprocessing,
объявляем свой класс, наследуемся от класса Thread,
в конструктор передаем нужные аргументы,
для того чтобы выполнить нашу функцию в отдельном потоке.
Запоминаем их в self.
Далее мы переопределяем метод run,
и метод run уже будет выполнен в отдельном потоке управления.
В этом методе run мы можем использовать
переданные аргументы конструктору, которые мы запомнили в self.
Далее создаем объект класса PrintThread,
передаем туда нужные аргументы,
в качестве примера я передал строку Mike.
Далее вызываем метод start.
Здесь всё то же самое.
Метод start создаст поток, и при помощи join мы будем ждать,
пока этот поток завершится в основном потоке.
Если мы исполним этот пример, то увидим вывод hello Mike.
Итак, вы можете использовать любой из приведенных способов,
всё зависит от ваших предпочтений, любите ли вы наследование
либо просто любите передавать функции как аргументы.
В Python3 появился очень удобный класс
для создания пула потоков.
Называется он ThreadPoolExecutor модуль concurrent.futures.
Предположим, у нас есть
некий массив чисел, и нам нужно ограничить
количество потоков и рассчитать
квадраты чисел для этого массива.
Для этого можно использовать контекстный менеджер,
указать в нем вызов ThreadPoolExecutor
с параметром max_workers, который как раз отвечает за
максимальное количество потоков,
которые будут созданы в этом блоке with.
Не нужно заботиться о завершении потоков.
Нужное количество потоков будет создано автоматически,
и при завершении контекстного менеджера
будет вызвана функция shutdown,
которая дождется завершения всех созданных потоков.
Основная функция у этого ThreadPoolExecutor
— это метод submit.
Метод submit создает объект класса concurrent.futures Future
— это такой объект, который еще не завершился,
но выполняется и будет завершен в будущем.
При помощи удобного метода as_completed
из этого модуля concurrent.futures
мы можем дождаться завершения всех наших объектов
и получить результаты по мере завершения всех этих потоков,
созданных нашим Executor.
Если мы запустим пример, мы опять получим вывод,
похожий на слайд.
Итак, в этом видео мы обсудили, что такое поток.
Мы рассмотрели особенности создания потоков при помощи модуля threading.
Ключевое отличие потоков от процессов состоит в том,
что они разделяют память и все ресурсы процессов,
в рамках которого они запущены.
Также мы рассмотрели работу класса ThreadPoolExecutor
и обсудили, как можно ограничить количество потоков
для решения конкретной задачи.
В следующем видео мы рассмотрим
вопрос синхронизации потоков и блокировки.