Подтвердить что ты не робот

Поддерживает ли Python многопоточность? Может ли это ускорить время выполнения?

Я немного смущен тем, работает ли многопоточность в Python или нет.

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

Многие плакаты/респонденты также отмечают, что потоковая передача ограничена, поскольку она не использует несколько ядер. Но я бы сказал, что они все еще полезны, потому что они работают одновременно и, таким образом, ускоряют совместную работу. Я имею в виду, почему в противном случае может существовать модуль потоков Python?

Update:

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

Я не совсем уверен, что это означает для меня в практическом плане, поэтому я просто приведу пример такой задачи, которую я хотел бы использовать для многопоточности. Например, допустим, что я хочу пройти через очень длинный список строк, и я хочу выполнить некоторые основные операции с строками в каждом элементе списка. Если я разделил список, отправьте каждый подсписок, который будет обработан моим циклом/строковым кодом в новом потоке, и отправьте результаты обратно в очередь, будут ли эти рабочие нагрузки загружаться в одно и то же время? Самое главное, это теоретически ускорит время, затрачиваемое на запуск script?

Другим примером может быть, если я смогу отображать и сохранять четыре разных изображения, используя PIL в четырех разных потоках, и иметь это быстрее, чем обрабатывать изображения по очереди один за другим? Я предполагаю, что эта составляющая скорости - это то, о чем я действительно задумываюсь, а не о правильной терминологии.

Я также знаю о модуле многопроцессорности, но сейчас мой основной интерес - это задачи с малой и средней нагрузкой (10-30 секунд), и поэтому я думаю, что многопоточность будет более уместной, поскольку подпроцессы могут быть медленными для запуска.

4b9b3361

Ответ 1

GIL не предотвращает потоки. Все GIL делает, чтобы только один поток выполнял код Python за раз; управление все еще переключается между потоками.

То, что GIL предотвращает, использует более одного ядра процессора или отдельных процессоров для параллельного запуска потоков.

Это относится только к коду Python. Расширения C могут и освобождают GIL, чтобы позволить нескольким потокам кода C и одному потоку Python работать через несколько ядер. Это распространяется на операции ввода/вывода, управляемые ядром, такие как select() вызовы для чтения и записи сокетов, что позволяет Python эффективно обрабатывать сетевые события в многопоточной многоядерной настройке.

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

Обратите внимание, что GIL применим только к реализации CPython; Jython и IronPython используют другую реализацию потоковой передачи (собственные встроенные потоки Java VM и .NET соответственно).

Для прямого обращения к вашему обновлению: любая задача, которая пытается получить ускорение скорости от параллельного выполнения, используя чистый код Python, не увидит ускорение, поскольку поточный код Python заблокирован для одного потока, выполняемого за один раз. Однако, если вы смешиваете только C-расширения и ввод-вывод (например, операции PIL или numpy), и любой C-код может работать параллельно с одним активным потоком Python.

Python threading отлично подходит для создания гибкого графического интерфейса или для обработки нескольких коротких веб-запросов, где I/O является узким местом больше, чем код Python. Он не подходит для распараллеливания вычислительно интенсивного кода Python, придерживаться модуля multiprocessing для таких задач или делегировать выделенную внешнюю библиотеку.

Ответ 2

Да.:)

У вас есть низкоуровневый thread модуль и более высокий уровень threading. Но вы просто хотите использовать многоядерные машины, способ multiprocessing - это путь.

Цитата из docs:

В CPython, из-за блокировки Global Interpreter Lock, только один поток может выполнить код Python сразу (хотя определенные ориентированные на производительность библиотеки могут преодолеть это ограничение). Если вы хотите приложения, чтобы лучше использовать вычислительные ресурсы многоядерные машины, рекомендуется использовать многопроцессорную обработку. Однако, Threading по-прежнему является подходящей моделью, если вы хотите запускать несколько Задачи, связанные с I/O одновременно.

Ответ 3

В Python разрешено создание потоков, единственная проблема заключается в том, что GIL будет гарантировать, что одновременно выполняется только один поток (без параллелизма).

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

Ответ 4

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

Имеет пул потоков и пул процессов.

Следующее дает понимание:

Пример ThreadPoolExecutor

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

ProcessPoolExecutor

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()