logo

Какво е GIL в Python? Глобално заключване на интерпретатора

Този урок ще бъде фокусиран върху една от важните теми за Python, GIL. Ще разгледаме също как GIL влияе върху производителността на програмите на Python с внедряването на кода. Преди да се потопим в тази тема, нека имаме основна представа за GIL.

GIL или Global Interpreter Lock

Python Global Interpreter Lock или GIL е важна част от многопоточното програмиране. Това е вид заключване на процес, използван при работа с множество процеси. Дава контрол само на една нишка. Обикновено Python използва една нишка за изпълнение на един процес. Получаваме същия резултат от производителността на еднонишковите и многонишковите процеси, използвайки GIL. Той ограничава постигането на многонишковост в Python, защото предотвратява нишките и работи като една нишка.

Забележка – Python не поддържа многопоточност, тъй като пакетите за нишки не могат да ни позволят да използваме множеството процесорни ядра.

Защо разработчиците на Python използват GIL?

Python предоставя уникалната функция за брояч на препратки, която се използва за управление на паметта. Броячът за препратки отчита общия брой препратки, направени вътрешно в Python, за да се присвои стойност на обект с данни. Когато броят на препратките достигне нула, присвоената памет на обекта се освобождава. Нека видим примера по-долу.

пример -

 import sys a = [] b = a sys.getrefcount(a) 

Основното безпокойство с променливата за броя на препратките е, че тя може да бъде засегната, когато две или три нишки се опитват да увеличат или намалят стойността й едновременно. Известно е като състояние на състезание. Ако възникне това състояние, то може да бъде причинено от изтекла памет, която никога не се освобождава. Може да се срине или да има грешки в програмата Python.

GIL ни помага да премахнем такава ситуация, като използваме ключалките за всички споделени структури от данни в нишките, така че да не се променят непоследователно. Python предоставя лесен начин за внедряване на GIL, тъй като се занимава с управление на паметта, безопасно за нишки. GIL изисква предлагане на едно заключване към нишка за обработка в Python. Повишава производителността на еднонишкова програма, тъй като само едно заключване изисква да бъде обработено. Той също така помага да се направи всяка програма, свързана с процесора, и предотвратява състоянието на блокиране.

Въздействието върху многонишковите програми на Python

Има разлика между границите на процесора в тяхната производителност и I/O обвързани за типична програма на Python или която и да е компютърна програма. Обвързаните с процесора програми обикновено изтласкват процесора до неговите граници. Тези програми обикновено се използват за математически изчисления като умножение на матрици, изпичане, обработка на изображения и др.

I/O обвързаните програми са тези програми, които отделят време за получаване на вход/изход, който може да бъде генериран от потребителя, файла, базата данни, мрежата и т.н. Такива програми трябва да изчакат известно време, докато източникът предостави входа. От друга страна, източникът също има собствено време за обработка. Например - потребителят мисли какво да въведе като вход.

Нека разберем следния пример.

пример -

маркировка html
 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Изход:

 Time taken in seconds - 7.422671556472778 

Сега променяме горния код, като стартираме двете нишки.

Пример - 2:

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Изход:

 Time taken in seconds - 6.90830135345459 

Както виждаме, и двата кода отнеха едно и също време, за да завършат. GIL попречи на нишките, обвързани с процесора, да се изпълняват паралелно във втория код.

Защо GIL все още не е премахнат?

Много програмисти имат оплаквания относно това, но Python не може да донесе промените толкова значителни, колкото премахването на GIL. Друга причина е, че GIL не е подобрен към момента. Ако се промени в Python 3, това ще създаде някои сериозни проблеми. Вместо премахване на GIL, концепцията на GIL може да се подобри. Според Гуидо ван Росом -

„Бих приветствал набор от корекции в Py3k само ако производителността за еднонишкова програма (и за многонишкова, но I/O-свързана програма) не намалява“.

Има и много налични методи, които решават същия проблем, решен от GIL, но има трудни за изпълнение.

Как да се справим с GIL на Python

Използването на мултипроцесиране е най-подходящият начин за предотвратяване на програмата от GIL. Python предлага различни интерпретатори за всеки процес, така че в този сценарий единичната нишка се предоставя на всеки процес при многопроцесорна обработка. Нека разберем следния пример.

пример -

 from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Изход:

 Time taken in seconds - 3.3707828521728516 

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

Заключение

В този урок обсъдихме GIL и как можем да го използваме. Той дава контрол на една нишка, която да се изпълнява наведнъж. Този урок също така обхвана защо GIL е важен за програмистите на Python.