Как дождаться завершения потока Python

PythonPythonBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Освоение навыков ожидания завершения потоков Python (Python threads) является важным для создания надежных и устойчивых приложений. В многопоточных программах правильная синхронизация гарантирует, что операции выполняются в правильном порядке и что ресурсы используются эффективно.

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


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/AdvancedTopicsGroup -.-> python/threading_multiprocessing("Multithreading and Multiprocessing") subgraph Lab Skills python/threading_multiprocessing -.-> lab-417461{{"Как дождаться завершения потока Python"}} end

Создание вашего первого потока Python

Модуль threading Python предоставляет простой способ создания и управления потоками. На этом шаге вы узнаете, как создать базовый поток и наблюдать за его поведением.

Понимание потоков

Поток (thread) — это отдельный поток выполнения в программе. Когда вы запускаете скрипт Python, он начинается с одного потока, называемого главным потоком (main thread). Создавая дополнительные потоки, ваша программа может выполнять несколько задач одновременно.

Потоки полезны для:

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

Создание простого потока

Давайте начнем с создания простого скрипта Python, который демонстрирует, как создать и запустить поток.

  1. Откройте новый файл в редакторе, нажав на меню "File", выбрав "New File" и сохранив его как simple_thread.py в каталоге /home/labex/project.

  2. Добавьте следующий код в файл:

import threading
import time

def print_numbers():
    """Function that prints numbers from 1 to 5 with a delay."""
    for i in range(1, 6):
        print(f"Number {i} from thread")
        time.sleep(1)  ## Sleep for 1 second

## Create a thread that targets the print_numbers function
number_thread = threading.Thread(target=print_numbers)

## Start the thread
print("Starting the thread...")
number_thread.start()

## Main thread continues execution
print("Main thread continues to run...")
print("Main thread is doing other work...")

## Sleep for 2 seconds to demonstrate both threads running concurrently
time.sleep(2)
print("Main thread finished its work!")
  1. Сохраните файл, нажав Ctrl+S или выбрав "File" > "Save".

  2. Запустите скрипт, открыв терминал (если он еще не открыт) и выполнив:

python3 /home/labex/project/simple_thread.py

Вы должны увидеть вывод, похожий на этот:

Starting the thread...
Main thread continues to run...
Main thread is doing other work...
Number 1 from thread
Number 2 from thread
Main thread finished its work!
Number 3 from thread
Number 4 from thread
Number 5 from thread

Анализ произошедшего

В этом примере:

  1. Мы импортировали модули threading и time.
  2. Мы определили функцию print_numbers(), которая выводит числа от 1 до 5 с задержкой в 1 секунду между каждым.
  3. Мы создали объект потока, указав функцию для запуска с помощью параметра target.
  4. Мы запустили поток, используя метод start().
  5. Главный поток продолжил свое выполнение, выводя сообщения и засыпая на 2 секунды.
  6. И главный поток, и наш поток чисел выполнялись одновременно, поэтому вывод чередуется.

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

На следующем шаге вы узнаете, как ждать завершения потока, используя метод join().

Ожидание завершения потока с помощью join()

На предыдущем шаге вы создали поток, который выполнялся независимо от главного потока. Однако во многих ситуациях вам нужно дождаться завершения работы потока, прежде чем переходить к остальной части вашей программы. Именно здесь метод join() становится полезным.

Понимание метода join()

Метод join() объекта потока блокирует вызывающий поток (обычно главный поток) до тех пор, пока поток, у которого вызывается метод join(), не завершится. Это необходимо, когда:

  • Главному потоку нужны результаты от рабочего потока
  • Вам нужно убедиться, что все потоки завершены, прежде чем выйти из программы
  • Порядок операций важен для логики вашего приложения

Создание потока и ожидание его завершения

Давайте изменим наш предыдущий пример, чтобы продемонстрировать, как ждать завершения потока, используя метод join().

  1. Создайте новый файл с именем join_thread.py в каталоге /home/labex/project.

  2. Добавьте следующий код в файл:

import threading
import time

def calculate_sum(numbers):
    """Function that calculates the sum of numbers with a delay."""
    print("Starting the calculation...")
    time.sleep(3)  ## Simulate a time-consuming calculation
    result = sum(numbers)
    print(f"Calculation result: {result}")
    return result

## Create a list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

## Create a thread that targets the calculate_sum function
calculation_thread = threading.Thread(target=calculate_sum, args=(numbers,))

## Start the thread
print("Main thread: Starting the calculation thread...")
calculation_thread.start()

## Do some other work in the main thread
print("Main thread: Doing some other work while waiting...")
for i in range(5):
    print(f"Main thread: Working... ({i+1}/5)")
    time.sleep(0.5)

## Wait for the calculation thread to complete
print("Main thread: Waiting for the calculation thread to finish...")
calculation_thread.join()
print("Main thread: Calculation thread has finished!")

## Continue with the main thread
print("Main thread: Continuing with the rest of the program...")
  1. Сохраните файл и запустите его следующей командой:
python3 /home/labex/project/join_thread.py

Вы должны увидеть вывод, похожий на этот:

Main thread: Starting the calculation thread...
Starting the calculation...
Main thread: Doing some other work while waiting...
Main thread: Working... (1/5)
Main thread: Working... (2/5)
Main thread: Working... (3/5)
Main thread: Working... (4/5)
Main thread: Working... (5/5)
Main thread: Waiting for the calculation thread to finish...
Calculation result: 55
Main thread: Calculation thread has finished!
Main thread: Continuing with the rest of the program...

Важность join()

В этом примере:

  1. Мы создали поток, который выполняет вычисление (суммирование чисел).
  2. Главный поток выполнял некоторую другую работу параллельно.
  3. Когда главному потоку нужно было убедиться, что вычисление завершено, он вызвал calculation_thread.join().
  4. Метод join() заставил главный поток ждать, пока поток вычислений не завершится.
  5. После завершения потока вычислений главный поток продолжил свое выполнение.

Этот шаблон очень полезен, когда вам нужно убедиться, что все потоковые задачи завершены, прежде чем переходить к остальной части вашей программы. Без join() главный поток может продолжить работу и даже завершиться до того, как рабочие потоки завершат свои задачи.

Использование join() с таймаутом

Иногда вам может потребоваться подождать поток, но не бесконечно. Метод join() принимает необязательный параметр таймаута, который указывает максимальное количество секунд для ожидания.

Давайте изменим наш код, чтобы продемонстрировать это:

  1. Создайте новый файл с именем join_timeout.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import threading
import time

def long_running_task():
    """A function that simulates a very long-running task."""
    print("Long-running task started...")
    time.sleep(10)  ## Simulate a 10-second task
    print("Long-running task completed!")

## Create and start the thread
task_thread = threading.Thread(target=long_running_task)
task_thread.start()

## Wait for the thread to complete, but only for up to 3 seconds
print("Main thread: Waiting for up to 3 seconds...")
task_thread.join(timeout=3)

## Check if the thread is still running
if task_thread.is_alive():
    print("Main thread: The task is still running, but I'm continuing anyway!")
else:
    print("Main thread: The task has completed within the timeout period.")

## Continue with the main thread
print("Main thread: Continuing with other operations...")
## Let's sleep a bit to see the long-running task complete
time.sleep(8)
print("Main thread: Finished.")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/join_timeout.py

Вывод должен выглядеть так:

Long-running task started...
Main thread: Waiting for up to 3 seconds...
Main thread: The task is still running, but I'm continuing anyway!
Main thread: Continuing with other operations...
Long-running task completed!
Main thread: Finished.

В этом примере главный поток ждет до 3 секунд, пока поток задачи не завершится. Поскольку задача занимает 10 секунд, главный поток продолжает работу после таймаута, в то время как поток задачи продолжает выполняться в фоновом режиме.

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

Работа с несколькими потоками

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

Создание нескольких потоков

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

Давайте создадим пример, который использует несколько потоков для обработки списка задач:

  1. Создайте новый файл с именем multiple_threads.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import threading
import time
import random

def process_task(task_id):
    """Function to process a single task."""
    print(f"Starting task {task_id}...")
    ## Simulate variable processing time
    processing_time = random.uniform(1, 3)
    time.sleep(processing_time)
    print(f"Task {task_id} completed in {processing_time:.2f} seconds.")
    return task_id

## List of tasks to process
tasks = list(range(1, 6))  ## Tasks with IDs 1 through 5

## Create a list to store our threads
threads = []

## Create and start a thread for each task
for task_id in tasks:
    thread = threading.Thread(target=process_task, args=(task_id,))
    threads.append(thread)
    print(f"Created thread for task {task_id}")
    thread.start()

print(f"All {len(threads)} threads have been started")

## Wait for all threads to complete
for thread in threads:
    thread.join()

print("All tasks have been completed!")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/multiple_threads.py

Вывод будет меняться каждый раз из-за случайного времени обработки, но должен выглядеть примерно так:

Created thread for task 1
Starting task 1...
Created thread for task 2
Starting task 2...
Created thread for task 3
Starting task 3...
Created thread for task 4
Starting task 4...
Created thread for task 5
Starting task 5...
All 5 threads have been started
Task 1 completed in 1.23 seconds.
Task 3 completed in 1.45 seconds.
Task 2 completed in 1.97 seconds.
Task 5 completed in 1.35 seconds.
Task 4 completed in 2.12 seconds.
All tasks have been completed!

Понимание потока выполнения

В этом примере:

  1. Мы определили функцию process_task(), которая имитирует обработку задачи со случайной продолжительностью.
  2. Мы создали список идентификаторов задач (от 1 до 5).
  3. Для каждой задачи мы создали поток, сохранили его в списке и запустили.
  4. После запуска всех потоков мы использовали второй цикл с join(), чтобы дождаться завершения каждого потока.
  5. Только после завершения всех потоков мы вывели окончательное сообщение.

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

Исполнители пула потоков (Thread Pool Executors)

Для более продвинутого управления потоками модуль concurrent.futures Python предоставляет класс ThreadPoolExecutor. Это создает пул рабочих потоков, которые можно повторно использовать, что более эффективно, чем создание и уничтожение потоков для каждой задачи.

Давайте перепишем наш пример, используя пул потоков:

  1. Создайте новый файл с именем thread_pool.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import concurrent.futures
import time
import random

def process_task(task_id):
    """Function to process a single task."""
    print(f"Starting task {task_id}...")
    ## Simulate variable processing time
    processing_time = random.uniform(1, 3)
    time.sleep(processing_time)
    print(f"Task {task_id} completed in {processing_time:.2f} seconds.")
    return f"Result of task {task_id}"

## List of tasks to process
tasks = list(range(1, 6))  ## Tasks with IDs 1 through 5

## Create a ThreadPoolExecutor
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    ## Submit all tasks and store the Future objects
    print(f"Submitting {len(tasks)} tasks to the thread pool with 3 workers...")
    future_to_task = {executor.submit(process_task, task_id): task_id for task_id in tasks}

    ## As each task completes, get its result
    for future in concurrent.futures.as_completed(future_to_task):
        task_id = future_to_task[future]
        try:
            result = future.result()
            print(f"Got result from task {task_id}: {result}")
        except Exception as e:
            print(f"Task {task_id} generated an exception: {e}")

print("All tasks have been processed!")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/thread_pool.py

Вывод снова будет меняться из-за случайного времени обработки, но должен выглядеть примерно так:

Submitting 5 tasks to the thread pool with 3 workers...
Starting task 1...
Starting task 2...
Starting task 3...
Task 2 completed in 1.15 seconds.
Starting task 4...
Got result from task 2: Result of task 2
Task 1 completed in 1.82 seconds.
Starting task 5...
Got result from task 1: Result of task 1
Task 3 completed in 2.25 seconds.
Got result from task 3: Result of task 3
Task 4 completed in 1.45 seconds.
Got result from task 4: Result of task 4
Task 5 completed in 1.67 seconds.
Got result from task 5: Result of task 5
All tasks have been processed!

Преимущества пулов потоков

Подход с использованием пула потоков предлагает несколько преимуществ:

  1. Управление ресурсами: Он ограничивает количество потоков, которые могут выполняться одновременно, предотвращая исчерпание системных ресурсов.
  2. Планирование задач: Он автоматически обрабатывает планирование задач, запуская новые задачи по мере доступности потоков.
  3. Сбор результатов: Он предоставляет удобные способы сбора результатов из завершенных задач.
  4. Обработка исключений: Он делает обработку исключений в потоках более простой.

В нашем примере мы установили max_workers=3, что означает, что одновременно будут выполняться только 3 потока, даже если у нас 5 задач. По мере завершения задач потоками они повторно используются для оставшихся задач.

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

Таймауты потоков и потоки-демоны

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

Работа с таймаутами потоков

Как вы узнали на шаге 2, метод join() принимает параметр таймаута. Это полезно, когда вы хотите дождаться завершения потока, но только до определенного момента.

Давайте создадим более практичный пример, в котором мы реализуем функцию, которая пытается получить данные с таймаутом:

  1. Создайте новый файл с именем thread_with_timeout.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import threading
import time
import random

def fetch_data(data_id):
    """Simulate fetching data that might take varying amounts of time."""
    print(f"Fetching data #{data_id}...")

    ## Simulate different fetch times, occasionally very long
    fetch_time = random.choices([1, 8], weights=[0.8, 0.2])[0]
    time.sleep(fetch_time)

    if fetch_time > 5:  ## Simulate a slow fetch
        print(f"Data #{data_id}: Fetch took too long!")
        return None
    else:
        print(f"Data #{data_id}: Fetch completed in {fetch_time} seconds!")
        return f"Data content for #{data_id}"

def fetch_with_timeout(data_id, timeout=3):
    """Fetch data with a timeout."""
    result = [None]  ## Using a list to store result from the thread

    def target_func():
        result[0] = fetch_data(data_id)

    ## Create and start the thread
    thread = threading.Thread(target=target_func)
    thread.start()

    ## Wait for the thread with a timeout
    thread.join(timeout=timeout)

    if thread.is_alive():
        print(f"Data #{data_id}: Fetch timed out after {timeout} seconds!")
        return "TIMEOUT"
    else:
        return result[0]

## Try to fetch several pieces of data
for i in range(1, 6):
    print(f"\nAttempting to fetch data #{i}")
    result = fetch_with_timeout(i, timeout=3)
    if result == "TIMEOUT":
        print(f"Main thread: Fetch for data #{i} timed out, moving on...")
    elif result is None:
        print(f"Main thread: Fetch for data #{i} completed but returned no data.")
    else:
        print(f"Main thread: Successfully fetched: {result}")

print("\nAll fetch attempts completed!")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/thread_with_timeout.py

Вывод будет меняться, но должен выглядеть примерно так:

Attempting to fetch data #1
Fetching data #1...
Data #1: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #1

Attempting to fetch data #2
Fetching data #2...
Data #2: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #2

Attempting to fetch data #3
Fetching data #3...
Data #3: Fetch timed out after 3 seconds!
Main thread: Fetch for data #3 timed out, moving on...
Data #3: Fetch took too long!

Attempting to fetch data #4
Fetching data #4...
Data #4: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #4

Attempting to fetch data #5
Fetching data #5...
Data #5: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #5

All fetch attempts completed!

Этот пример демонстрирует:

  1. Функцию, которая пытается получить данные и может быть медленной
  2. Функцию-обертку, которая использует потоки с таймаутом
  3. Как корректно обрабатывать таймауты и продолжать другие операции

Понимание потоков-демонов

В Python потоки-демоны — это потоки, которые работают в фоновом режиме. Ключевое различие между потоками-демонами и недемонскими потоками заключается в том, что Python не будет ждать завершения потоков-демонов перед выходом. Это полезно для потоков, которые выполняют фоновые задачи, которые не должны мешать выходу из программы.

Давайте создадим пример, чтобы продемонстрировать потоки-демоны:

  1. Создайте новый файл с именем daemon_threads.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import threading
import time

def background_task(name, interval):
    """A task that runs in the background at regular intervals."""
    count = 0
    while True:
        count += 1
        print(f"{name}: Iteration {count} at {time.strftime('%H:%M:%S')}")
        time.sleep(interval)

def main_task():
    """The main task that runs for a set amount of time."""
    print("Main task: Starting...")
    time.sleep(5)
    print("Main task: Completed!")

## Create two background threads
print("Creating background monitoring threads...")
monitor1 = threading.Thread(target=background_task, args=("Monitor-1", 1), daemon=True)
monitor2 = threading.Thread(target=background_task, args=("Monitor-2", 2), daemon=True)

## Start the background threads
monitor1.start()
monitor2.start()

print("Background monitors started, now starting main task...")

## Run the main task
main_task()

print("Main task completed, program will exit without waiting for daemon threads.")
print("Daemon threads will be terminated when the program exits.")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/daemon_threads.py

Вывод должен выглядеть примерно так:

Creating background monitoring threads...
Background monitors started, now starting main task...
Main task: Starting...
Monitor-1: Iteration 1 at 14:25:10
Monitor-2: Iteration 1 at 14:25:10
Monitor-1: Iteration 2 at 14:25:11
Monitor-1: Iteration 3 at 14:25:12
Monitor-2: Iteration 2 at 14:25:12
Monitor-1: Iteration 4 at 14:25:13
Monitor-1: Iteration 5 at 14:25:14
Monitor-2: Iteration 3 at 14:25:14
Main task: Completed!
Main task completed, program will exit without waiting for daemon threads.
Daemon threads will be terminated when the program exits.

В этом примере:

  1. Мы создали два потока-демона, которые работают непрерывно, выводя сообщения через регулярные промежутки времени.
  2. Мы установили daemon=True при создании потоков, что помечает их как потоки-демоны.
  3. Главный поток работает 5 секунд, а затем завершается.
  4. Когда главный поток завершается, программа завершается, и потоки-демоны также автоматически завершаются.

Недемонские потоки против потоков-демонов

Чтобы лучше понять разницу, давайте создадим еще один пример, который сравнивает потоки-демоны и недемонские потоки:

  1. Создайте новый файл с именем daemon_comparison.py в каталоге /home/labex/project.

  2. Добавьте следующий код:

import threading
import time

def task(name, seconds, daemon=False):
    """A task that runs for a specified amount of time."""
    print(f"{name} starting {'(daemon)' if daemon else '(non-daemon)'}")
    time.sleep(seconds)
    print(f"{name} finished after {seconds} seconds")

## Create a non-daemon thread that runs for 8 seconds
non_daemon_thread = threading.Thread(
    target=task,
    args=("Non-daemon thread", 8, False),
    daemon=False  ## This is the default, so it's not actually needed
)

## Create a daemon thread that runs for 8 seconds
daemon_thread = threading.Thread(
    target=task,
    args=("Daemon thread", 8, True),
    daemon=True
)

## Start both threads
non_daemon_thread.start()
daemon_thread.start()

## Let the main thread run for 3 seconds
print("Main thread will run for 3 seconds...")
time.sleep(3)

## Check which threads are still running
print("\nAfter 3 seconds:")
print(f"Daemon thread is alive: {daemon_thread.is_alive()}")
print(f"Non-daemon thread is alive: {non_daemon_thread.is_alive()}")

print("\nMain thread is finishing. Here's what will happen:")
print("1. The program will wait for all non-daemon threads to complete")
print("2. Daemon threads will be terminated when the program exits")

print("\nWaiting for non-daemon threads to finish...")
## We don't need to join the non-daemon thread, Python will wait for it
## But we'll explicitly join it for clarity
non_daemon_thread.join()
print("All non-daemon threads have finished, program will exit now.")
  1. Сохраните файл и запустите его:
python3 /home/labex/project/daemon_comparison.py

Вывод должен выглядеть так:

Non-daemon thread starting (non-daemon)
Daemon thread starting (daemon)
Main thread will run for 3 seconds...

After 3 seconds:
Daemon thread is alive: True
Non-daemon thread is alive: True

Main thread is finishing. Here's what will happen:
1. The program will wait for all non-daemon threads to complete
2. Daemon threads will be terminated when the program exits

Waiting for non-daemon threads to finish...
Non-daemon thread finished after 8 seconds
All non-daemon threads have finished, program will exit now.

Основные наблюдения:

  1. Оба потока запускаются и работают параллельно.
  2. Через 3 секунды оба потока все еще работают.
  3. Программа ждет завершения недемонского потока (через 8 секунд).
  4. Поток-демон все еще работает, когда программа завершается, но он завершается.
  5. Поток-демон никогда не успевает напечатать свое сообщение о завершении, потому что он завершается, когда программа завершается.

Когда использовать потоки-демоны

Потоки-демоны полезны для:

  • Фоновых задач мониторинга
  • Операций очистки
  • Служб, которые должны работать в течение всей программы, но не мешать ее выходу
  • Потоков таймера, которые запускают события через регулярные промежутки времени

Недемонские потоки подходят для:

  • Критических операций, которые должны быть завершены
  • Задач, которые не должны быть прерваны
  • Операций, которые должны быть чисто завершены до выхода из программы

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

Резюме

В этой лабораторной работе вы изучили основные методы работы с потоками Python и способы ожидания их завершения. Вот краткое изложение основных рассмотренных концепций:

  1. Создание и запуск потоков: Вы узнали, как создать объект потока, указать целевую функцию и запустить ее выполнение с помощью метода start().

  2. Ожидание потоков с помощью join(): Вы узнали, как использовать метод join() для ожидания завершения потока, прежде чем продолжить работу с основной программой, обеспечивая надлежащую синхронизацию.

  3. Работа с несколькими потоками: Вы попрактиковались в создании и управлении несколькими потоками, как вручную, так и с использованием класса ThreadPoolExecutor для более эффективного управления потоками.

  4. Таймауты потоков и потоки-демоны: Вы изучили продвинутые темы, включая установку таймаутов для операций с потоками и использование потоков-демонов для фоновых задач.

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

Продолжая работать с потоками, помните об этих лучших практиках:

  • Используйте потоки для задач, зависящих от ввода-вывода, а не для задач, зависящих от процессора (рассмотрите возможность использования многопроцессорности для последних)
  • Помните об общих ресурсах и используйте соответствующие механизмы синхронизации
  • Рассмотрите возможность использования абстракций более высокого уровня, таких как ThreadPoolExecutor, для управления несколькими потоками
  • Используйте потоки-демоны для фоновых задач, которые не должны мешать выходу из программы

Обладая этими навыками и практиками, вы теперь готовы создавать более эффективные и отзывчивые приложения Python, используя методы многопоточности.