Чи підтримує Python багатопотоковість? Чи може це прискорити час виконання?


95

Я трохи заплутаний у тому, працює багатопоточність у Python чи ні.

Я знаю, що з цього приводу було багато питань, і я їх багато читав, але все ще бентежусь. Я знаю з власного досвіду і бачив, як інші публікують власні відповіді та приклади тут, на StackOverflow, що багатопотоковість дійсно можлива в Python. То чому всі постійно кажуть, що Python заблокований GIL і що одночасно може працювати лише один потік? Очевидно, це працює. Або є якась різниця, яку я тут не отримую?

Багато плакатів / респондентів також постійно згадують, що потокове обмеження обмежене, оскільки воно не використовує кілька ядер. Але я б сказав, що вони все ще корисні, оскільки вони працюють одночасно, і таким чином швидше виконують комбіноване навантаження. Я маю на увазі, чому в іншому випадку взагалі існував би модуль потоку Python?

Оновлення:

Дякую за всі відповіді на сьогодні. Я розумію, що багатопотоковість буде виконуватися паралельно лише для деяких завдань вводу-виводу, але може виконуватися лише по одній для кількох основних завдань, пов'язаних з процесором.

Я не зовсім впевнений, що це означає для мене на практиці, тому я просто наведу приклад того типу завдання, яку я хотів би багатопотоковою. Наприклад, скажімо, я хочу прокрутити дуже довгий список рядків, і я хочу виконати деякі основні операції з рядками для кожного елемента списку. Якщо я розділю список, відправлю кожен підсписок на обробку моїм кодом циклу / рядка в новому потоці та відправляю результати назад у чергу, чи будуть ці робочі навантаження працювати приблизно одночасно? Найголовніше, чи теоретично це пришвидшить час, необхідний для запуску сценарію?

Іншим прикладом може бути, якщо я можу візуалізувати та зберегти чотири різні картинки за допомогою PIL у чотирьох різних потоках, і чи це буде швидше, ніж обробка зображень одна за одною одна за одною? Я думаю, що ця швидкісна складова - це те, що мені насправді цікаво, а не яка правильна термінологія.

Я також знаю про модуль багатопроцесорної обробки, але зараз мій головний інтерес полягає у малих та середніх навантаженнях завдань (10-30 секунд), і тому я думаю, що багатопотоковість буде доцільнішою, оскільки ініціювати підпроцеси може повільно.


4
Це досить завантажене запитання. Я думаю, що відповідь полягає в тому, що ви хочете, щоб нитки робили. За більшості обставин GIL запобігає одночасному запуску більше 1 потоку. Однак є кілька випадків, коли GIL випускається (наприклад, зчитування з файлу), що можна робити паралельно. Також зверніть увагу, що GIL - це деталь реалізації Cpython (найпоширеніша реалізація). Жодна інша реалізація python (Jython, PyPy тощо) не має GIL (AFAIK)
mgilson

2
@mgilson PyPy має GIL.

2
@delnan - Ви здаєтеся правильно. Дякую.
mgilson

1
"підпроцеси можуть ініціюватися повільно" - ви можете створити пул завдань, готових до виконання. Накладні витрати можуть бути обмежені приблизно кількістю часу, необхідного для серіалізації / десериалізації даних, необхідних для того, щоб завдання почало працювати.
Брайан Кейн

1
@KarimBahgat, я маю на увазі саме це.
Брайан Кейн

Відповіді:


132

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 чудово підходять для створення адаптивного графічного інтерфейсу або для обробки кількох коротких веб-запитів, де введення / виведення є вузьким місцем більше, ніж код Python. Він не підходить для розпаралелювання обчислювально інтенсивного коду Python, дотримуйтесь multiprocessingмодуля для таких завдань або делегуйте до виділеної зовнішньої бібліотеки.


Дякую @MartijnPieters, тоді я маю більш чітку відповідь на своє запитання про те, чи можна використовувати різьбу для пришвидшення коду, такого як for-loop, що "ні". Можливо, ви або хтось міг би написати нову відповідь, яку я можу прийняти, яка містить деякі конкретні приклади загальних модулів / кодів / операцій, де GIL дозволить потоковій роботі запускати паралель і, отже, швидше (наприклад, приклади цих входів / виходів та мережі / згадані операції зчитування сокета та будь-які інші випадки, коли багатопотоковість у Python корисна). Можливо, приємний перелік поширених багатопотокових застосувань та деякі приклади програмування, якщо це можливо?
Карім Бахгат

4
Ні, я не думаю, що така відповідь була б дуже корисною; чесно кажучи. Ви не можете створити вичерпний список ніколи, але основним правилом є те, що будь-який ввід / вивід (читання та запис файлів, мережеві розетки, канали) обробляється на C, і багато бібліотек C також випускають GIL для своїх операцій, але бібліотеки самі повинні це задокументувати.
Мартін Пітерс

1
Мой поганий, я дотепер не бачив вашої оновленої відповіді, де ви наводили кілька приємних прикладів використання ниток. Сюди входило (виправте мене, якщо помиляюсь) мережеве програмування (наприклад urllib.urlopen()?), Image.transform()Викликати один сценарій Python із графічного інтерфейсу Python та викликати кілька операцій PIL (наприклад ) та numpy (наприклад numpy.array()) з потоками. І ви надали ще кілька прикладів у своєму коментарі, наприклад, використання декількох потоків для читання файлів (наприклад f.read()?). Я знаю, що вичерпний список неможливий, я просто хотів типи прикладів, які ви наводили у своєму оновленні. У будь-якому випадку, прийняв вашу відповідь :)
Карім Бахгат

2
@KarimBahgat: Так, urllib.urlopen()буде викликати мережеві сокети, очікування вводу-виводу сокета - це відмінна можливість переключити потоки та зробити щось інше.
Мартін Пітерс

4
Хоча це безпосередньо не стосується цієї проблеми, варто зауважити, що іноді створення потоків - це зовсім не продуктивність; можливо, простіше написати свій код як кілька незалежних потоків виконання. Наприклад, у вас може бути одна нитка, яка відтворює фонову музику, одна, яка обслуговує інтерфейс, і одна, яка відмовляється від обчислень, які з часом потрібно зробити, але не поспішають. Спроба послідовного відтворення наступного звукового буфера за допомогою циклу інтерфейсу користувача або розбити обчислення на досить дрібні фрагменти, щоб не заважати інтерактивності, може бути набагато складніше, ніж використання потоків.
abarnert

4

Так. :)

У вас є модуль нитки низького рівня та вищий рівень модуль різьби . Але якщо ви просто хочете використовувати багатоядерні машини, багатопроцесорний модуль - це шлях.

Цитата з документації :

У CPython, завдяки Global Interpreter Lock, лише один потік може виконувати код Python одночасно (навіть якщо певні бібліотеки, орієнтовані на продуктивність, можуть подолати це обмеження). Якщо ви хочете, щоб ваша програма краще використовувала обчислювальні ресурси багатоядерних машин, вам рекомендується використовувати багатопроцесорну обробку. Однак потокова передача все ще є відповідною моделлю, якщо ви хочете одночасно запускати кілька завдань, пов'язаних з введенням / виведенням.


3

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

Отже, якщо ви хочете багатопотоковий код для пришвидшення обчислення, він не прискорить його, оскільки виконується лише один потік за раз, але якщо ви використовуєте його для взаємодії з базою даних, наприклад, це буде.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.