Многопоточность в Python на примере:изучаем GIL в Python
Язык программирования Python позволяет использовать многопроцессорность или многопоточность. В этом руководстве вы узнаете, как писать многопоточные приложения на Python.
Что такое поток?
Поток — это единица выполнения параллельного программирования. Многопоточность — это метод, который позволяет ЦП выполнять множество задач одного процесса одновременно. Эти потоки могут выполняться индивидуально при совместном использовании ресурсов процесса.
Что такое процесс?
Процесс — это, по сути, исполняемая программа. Когда вы запускаете приложение на своем компьютере (например, браузер или текстовый редактор), операционная система создает процесс.
Что такое многопоточность в Python?
Многопоточность в Python программирование — это хорошо известный метод, при котором несколько потоков в процессе совместно используют свое пространство данных с основным потоком, что делает обмен информацией и связь внутри потоков простыми и эффективными. Потоки легче процессов. Многопоточность может выполняться индивидуально при совместном использовании ресурсов процесса. Цель многопоточности — одновременное выполнение нескольких задач и функциональных ячеек.
В этом уроке вы узнаете,
- Что такое поток?
- Что такое процесс?
- Что такое многопоточность?
- Что такое многопроцессорность?
- Многопоточность Python и многопроцессорность
- Зачем использовать многопоточность?
- Многопоточность Python
- Модули Thread и Threading
- Модуль потока
- Модуль потоков
- Взаимоблокировки и условия гонки
- Синхронизация потоков
- Что такое GIL?
- Зачем был нужен GIL?
Что такое многопроцессорность?
Многопроцессорность позволяет запускать несколько несвязанных процессов одновременно. Эти процессы не делятся своими ресурсами и взаимодействуют через IPC.
Многопоточность Python и многопроцессорность
Чтобы понять процессы и потоки, рассмотрим следующий сценарий. Файл .exe на вашем компьютере — это программа. Когда вы открываете его, ОС загружает его в память, а ЦП выполняет. Экземпляр программы, которая сейчас выполняется, называется процессом.
Каждый процесс будет состоять из двух основных компонентов:
- Кодекс
- Данные
Теперь процесс может содержать одну или несколько частей, называемых потоками. Это зависит от архитектуры ОС. Вы можете думать о потоке как о части процесса, которая может выполняться операционной системой отдельно.
Другими словами, это поток инструкций, который ОС может выполнять независимо. Потоки внутри одного процесса совместно используют данные этого процесса и предназначены для совместной работы для облегчения параллелизма.
Зачем использовать многопоточность?
Многопоточность позволяет разбить приложение на несколько подзадач и выполнять эти задачи одновременно. Если вы правильно используете многопоточность, скорость вашего приложения, производительность и отрисовка могут быть улучшены.
Многопоточность Python
Python поддерживает конструкции как для многопроцессорной обработки, так и для многопоточности. В этом руководстве вы в первую очередь сосредоточитесь на реализации многопоточных приложения с питоном. Есть два основных модуля, которые можно использовать для обработки потоков в Python:
- тред модуль и
- потоки модуль
Однако в Python также есть нечто, называемое глобальной блокировкой интерпретатора (GIL). Это не дает значительного прироста производительности и может даже снижать производительность некоторых многопоточных приложений. Вы узнаете все об этом в следующих разделах этого руководства.
Модули Thread и Threading
В этом руководстве вы узнаете о двух модулях:модуль потока. и модуль потоков .
Однако модуль thread уже давно устарел. Начиная с Python 3, он считается устаревшим и доступен только как __thread. для обратной совместимости.
Вы должны использовать потоки более высокого уровня. модуль для приложений, которые вы собираетесь развернуть. Модуль потока описан здесь только в образовательных целях.
Модуль потока
Синтаксис для создания нового потока с использованием этого модуля следующий:
thread.start_new_thread(function_name, arguments)
Хорошо, теперь вы изучили основную теорию, чтобы начать программировать. Итак, откройте IDLE или блокнот и введите следующее:
import time import _thread def thread_test(name, wait): i = 0 while i <= 3: time.sleep(wait) print("Running %s\n" %name) i = i + 1 print("%s has finished execution" %name) if __name__ == "__main__": _thread.start_new_thread(thread_test, ("First Thread", 1)) _thread.start_new_thread(thread_test, ("Second Thread", 2)) _thread.start_new_thread(thread_test, ("Third Thread", 3))
Сохраните файл и нажмите F5, чтобы запустить программу. Если все было сделано правильно, вы должны увидеть следующий вывод:
Вы узнаете больше об условиях гонки и о том, как с ними справляться, в следующих разделах
ОБЪЯСНЕНИЕ КОДА
- Эти операторы импортируют модуль времени и потока, которые используются для управления выполнением и задержкой потоков Python.
- Здесь вы определили функцию с именем thread_test. который будет вызываться start_new_thread метод. Функция выполняет цикл while для четырех итераций и печатает имя потока, который ее вызвал. После завершения итерации выводится сообщение о том, что выполнение потока завершено.
- Это основной раздел вашей программы. Здесь вы просто вызываете start_new_thread метод с thread_test функция в качестве аргумента. Это создаст новый поток для функции, которую вы передаете в качестве аргумента, и начнет ее выполнение. Обратите внимание, что вы можете заменить это (thread_ test) с любой другой функцией, которую вы хотите запустить как поток.
Модуль потоков
Этот модуль представляет собой высокоуровневую реализацию многопоточности в Python и стандарт де-факто для управления многопоточными приложениями. Он предоставляет широкий спектр возможностей по сравнению с модулем потока.
Вот список некоторых полезных функций, определенных в этом модуле:
Имя функции | Описание |
---|---|
activeCount() | Возвращает количество Thread объекты, которые еще живы |
currentThread() | Возвращает текущий объект класса Thread. |
перечисление() | Список всех активных объектов Thread. |
isDaemon() | Возвращает true, если поток является демоном. |
являетсяживым() | Возвращает true, если поток все еще жив. |
Методы класса потока | |
начать() | Начинает активность потока. Его нужно вызывать только один раз для каждого потока, потому что при многократном вызове он вызовет ошибку времени выполнения. |
выполнить() | Этот метод обозначает активность потока и может быть переопределен классом, расширяющим класс Thread. |
присоединиться() | Он блокирует выполнение другого кода до тех пор, пока поток, в котором был вызван метод join(), не будет завершен. |
Предыстория:класс Thread
Прежде чем приступить к кодированию многопоточных программ с использованием модуля threading, очень важно понять класс Thread. Класс потока — это основной класс, который определяет шаблон и операции потока в Python.
Наиболее распространенный способ создания многопоточного приложения Python — объявить класс, который расширяет класс Thread и переопределяет его метод run().
Короче говоря, класс Thread означает последовательность кода, которая выполняется в отдельном потоке. контроля.
Итак, при написании многопоточного приложения вы будете делать следующее:
- определить класс, расширяющий класс Thread
- Переопределить __init__ конструктор
- Переопределить run() метод
После создания объекта потока функция start() метод можно использовать для начала выполнения этого действия, а метод join() можно использовать для блокировки всего остального кода до завершения текущего действия.
Теперь давайте попробуем использовать модуль threading для реализации вашего предыдущего примера. Снова запустите IDLE и введите следующее:
import time import threading class threadtester (threading.Thread): def __init__(self, id, name, i): threading.Thread.__init__(self) self.id = id self.name = name self.i = i def run(self): thread_test(self.name, self.i, 5) print ("%s has finished execution " %self.name) def thread_test(name, wait, i): while i: time.sleep(wait) print ("Running %s \n" %name) i = i - 1 if __name__=="__main__": thread1 = threadtester(1, "First Thread", 1) thread2 = threadtester(2, "Second Thread", 2) thread3 = threadtester(3, "Third Thread", 3) thread1.start() thread2.start() thread3.start() thread1.join() thread2.join() thread3.join()
Это будет вывод, когда вы выполните приведенный выше код:
ОБЪЯСНЕНИЕ КОДА
- Эта часть аналогична предыдущему примеру. Здесь вы импортируете модуль времени и потока, которые используются для обработки выполнения и задержек потоков Python.
- В этом разделе вы создаете класс threadtester, который наследует или расширяет класс Thread. класс модуля потоков. Это один из самых распространенных способов создания потоков в Python. Однако вам следует переопределить только конструктор и функцию run(). метод в вашем приложении. Как видно из приведенного выше примера кода, __init__ метод (конструктор) был переопределен. Точно так же вы также переопределили run() метод. Он содержит код, который вы хотите выполнить внутри потока. В этом примере вы вызвали функцию thread_test().
- Это метод thread_test(), который принимает значение i в качестве аргумента, уменьшает его на 1 на каждой итерации и перебирает остальную часть кода до тех пор, пока i не станет 0. На каждой итерации он печатает имя текущего выполняющегося потока и приостанавливается на секунды ожидания (что также принимается в качестве аргумента ).
- thread1 =threadtester(1, «Первая нить», 1) Здесь мы создаем нить и передаем три параметра, которые мы объявили в __init__. Первый параметр — это идентификатор потока, второй параметр — это имя потока, а третий параметр — это счетчик, который определяет, сколько раз должен выполняться цикл while.
- thread2.start()Метод запуска используется для запуска выполнения потока. Внутри функция start() вызывает метод run() вашего класса.
- thread3.join() Метод join() блокирует выполнение другого кода и ожидает завершения потока, в котором он был вызван.
Как вы уже знаете, потоки, находящиеся в одном процессе, имеют доступ к памяти и данным этого процесса. В результате, если несколько потоков пытаются одновременно изменить или получить доступ к данным, могут возникнуть ошибки.
В следующем разделе вы увидите различные виды осложнений, которые могут возникнуть, когда потоки обращаются к данным и критическому разделу без проверки существующих транзакций доступа.
Взаимоблокировки и условия гонки
Прежде чем изучать взаимоблокировки и условия гонки, будет полезно понять несколько основных определений, связанных с параллельным программированием:
- Критическая секцияЭто фрагмент кода, который обращается к общим переменным или изменяет их и должен выполняться как атомарная транзакция.
- Переключение контекстаЭто процесс, которому ЦП следует для сохранения состояния потока перед переключением с одной задачи на другую, чтобы его можно было возобновить с той же точки позже.
Взаимоблокировки
Взаимоблокировки — самая опасная проблема, с которой сталкиваются разработчики при написании параллельных/многопоточных приложений на python. Лучший способ разобраться в тупиковых ситуациях — использовать классическую задачу из области компьютерных наук, известную как задача обедающих философов.
Постановка задачи для обедающих философов выглядит следующим образом:
Пять философов сидят за круглым столом с пятью тарелками спагетти (тип пасты) и пятью вилками, как показано на диаграмме.
Python
- Функция free() в библиотеке C:как использовать? Учитесь на примере
- Функция Python String strip() с ПРИМЕРОМ
- Количество строк Python() с ПРИМЕРАМИ
- Функция Python round() с ПРИМЕРАМИ
- Функция Python map() с ПРИМЕРАМИ
- Python Timeit() с примерами
- Счетчик Python в коллекциях с примером
- Счетчик списка Python() с ПРИМЕРАМИ
- Индекс списка Python() с примером
- С# — многопоточность