threading
:
import threading
def my_task():
# Код задачи, которую нужно выполнить
print("Задача выполняется в отдельном потоке")
# Создание и запуск потока
thread = threading.Thread(target=my_task)
thread.start()
# Основной поток продолжает выполнение
print("Основной поток продолжает работу")
Альтернативно можно использовать concurrent.futures.ThreadPoolExecutor
для более удобного управления потоками.
Есть несколько способов запустить задачу в отдельном потоке в Python, не блокируя основной поток. Вот основные:
1. Использование модуля threading
:
Модуль threading
предоставляет низкоуровневый доступ к управлению потоками. Он позволяет создавать и запускать потоки, которые выполняются параллельно с основным потоком.
import threading
import time
def worker(arg):
"""Функция, которая будет выполняться в отдельном потоке."""
print(f"Поток worker: начало работы с аргументом {arg}")
time.sleep(2) # Имитация долгой задачи
print(f"Поток worker: завершение работы с аргументом {arg}")
if __name__ == "__main__":
print("Основной поток: начало работы")
# Создаем объект Thread, передавая функцию worker и аргументы
thread = threading.Thread(target=worker, args=(10,))
# Запускаем поток
thread.start()
print("Основной поток: продолжает работу")
time.sleep(1) # Имитация работы основного потока
print("Основной поток: завершение работы")
# (Опционально) Дождаться завершения потока worker
thread.join()
print("Основной поток: все потоки завершены")
Важные моменты:
threading.Thread(target=function, args=(arguments,))
: Создает новый поток, который будет выполнять функцию function
с аргументами arguments
. Обратите внимание, что args
должен быть кортежем.thread.start()
: Запускает поток. Фактическое выполнение функции worker
начинается параллельно с основным потоком.thread.join()
: Основной поток ждет завершения потока thread
. Если join()
не вызван, основной поток может завершиться раньше, чем поток worker
. Использование join()
гарантирует, что все потоки будут завершены перед завершением программы. join()
принимает необязательный аргумент timeout
, позволяющий указать максимальное время ожидания завершения потока.2. Использование concurrent.futures
:
Модуль concurrent.futures
предоставляет высокоуровневый интерфейс для асинхронного выполнения задач, включая использование потоков и процессов.
import concurrent.futures
import time
def worker(arg):
"""Функция, которая будет выполняться."""
print(f"Функция worker: начало работы с аргументом {arg}")
time.sleep(2) # Имитация долгой задачи
print(f"Функция worker: завершение работы с аргументом {arg}")
return arg * 2
if __name__ == "__main__":
print("Основной поток: начало работы")
# Создаем объект ThreadPoolExecutor
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
# Отправляем задачу на выполнение
future = executor.submit(worker, 10)
print("Основной поток: продолжает работу")
time.sleep(1) # Имитация работы основного потока
print("Основной поток: завершение работы")
# Получаем результат (если нужно)
result = future.result()
print(f"Результат выполнения worker: {result}")
print("Основной поток: все задачи завершены")
Важные моменты:
concurrent.futures.ThreadPoolExecutor(max_workers=N)
: Создает пул потоков, готовых к выполнению задач. max_workers
определяет максимальное количество потоков в пуле.executor.submit(function, *args)
: Отправляет функцию function
с аргументами *args
на выполнение в одном из потоков пула. Возвращает объект Future
, представляющий результат выполнения задачи.future.result()
: Блокирует выполнение до получения результата из потока. Можно использовать future.done()
для проверки, завершилась ли задача, и future.exception()
для получения исключения, если оно возникло.with
гарантирует, что пул потоков будет корректно завершен после выполнения всех задач.Выбор между threading
и concurrent.futures
:
threading
: Предоставляет более низкоуровневый контроль над потоками, полезно для сложных сценариев, требующих точного управления.concurrent.futures
: Предлагает более простой и удобный интерфейс для запуска задач в потоках или процессах, особенно для параллельного выполнения нескольких независимых задач. Рекомендуется для большинства случаев.Важно помнить о Global Interpreter Lock (GIL):
Python имеет GIL, который позволяет только одному потоку выполнять байткод Python одновременно. Это означает, что для задач, интенсивно использующих CPU, использование потоков может не привести к значительному приросту производительности. В таких случаях рекомендуется использовать процессы (модуль multiprocessing
или concurrent.futures.ProcessPoolExecutor
), которые обходят GIL.
Для задач, связанных с ожиданием ввода-вывода (например, сетевые запросы, чтение файлов), потоки все равно могут быть полезны, поскольку GIL освобождается во время ожидания.