Sobes.tech
Middle

Ի՞նչ է GIL (Global Interpreter Lock)-ը Python-ում։

sobes.tech AI

Պատասխան AI-ից

GIL (Աշխարհագրական Ինտերպրետատորի Լոկ) — դա մեխանիզմ է CPython ինտերպրետատորում, որը թույլ է տալիս կատարել միայն մեկ թել (thread) մեկ ժամանակում: Թեև շատականյա պրոցեսորների վրա, GIL-ը չի տալիս թելերին իրականում միաժամանակյա Python կոդի գործարկում:

GIL-ի գոյության պատճառները:

  • Հիշողության կառավարման պարզեցում: GIL-ը հեշտացնում է թելային անվտանգ հիշողության կառավարման և աղբի հավաքման (reference counting) իրականացումը: GIL-ի բացակայության դեպքում անհրաժեշտ կլինեին ավելի բարդ և ռեսուրսներ պահանջող սեղմման մեխանիզմներ՝ ընդհանուր տվյալների կառուցվածքները պաշտպանելու համար:
  • Կայունություն C գրադարանների հետ: GIL-ը հեշտացնում է ինտեգրացիան C գրադարանների հետ, որոնք կարող են չլինել թելային անվտանգ:

GIL-ի ազդեցությունը:

  • CPU-կապված առաջադրանքների պառլելիզմի սահմանափակում: CPU-կապված առաջադրանքների դեպքում, GIL-ը դառնում է խոչընդոտ, քանի որ նույնիսկ շատականյա համակարգերում, գործարկումը չի կատարվում միաժամանակ տարբեր միջուկների վրա: Թելերը պետք է սպասեն, մինչև GIL-ը ազատվի:
  • Փոքր ազդեցություն I/O-կապված առաջադրանքների վրա: I/O-կապված առաջադրանքների դեպքում, GIL-ը քիչ է ազդում, քանի որ այն ազատվում է sleep ժամանակ:

GIL-ից խուսափելու միջոցները:

  • Բազմամշակութային (Multiprocessing): Փոխարենը թելերի, օգտագործվում են անկախ պրոցեսներ: Յուրաքանչյուր պրոցես ունի իր Python ինտերպրետատորի օրինակ և, հետևաբար, իր GIL-ը: Պրոցեսները կարող են գործարկել միաժամանակ տարբեր միջուկների վրա:
  • Ասինխրոն ծրագրավորում (asyncio): Օգտագործում է ասինխրոն I/O մոդել մեկ թելում: Փոխարեն շատ թելեր ստեղծելու, ասինխրոն կոդը փոխում է առաջադրանքների միջև I/O սպասման ժամանակ:
  • C/C++ գրադարաններ: Կոդի մասերը, որոնք գրված են C կամ C++, կարող են կատարել գործողություններ առանց GIL պահելու, ազատելով այն այդ գործողությունների ժամանակ:
# GIL-ի ազդեցության օրինակ CPU-կապված առաջադրանքի վրա
import threading
import time

def cpu_bound_task():
    count = 0
    for i in range(10**7):
        count += 1

# Երկու թել ստեղծում CPU-կապված առաջադրանքի համար
t1 = threading.Thread(target=cpu_bound_task)
t2 = threading.Thread(target=cpu_bound_task)

սկիզբ = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
ավարտ = time.time()

print(f"Անցած ժամանակ թելերով (CPU-կապված): {ավարտ - սկիզբ:.2f} վայրկյան") # Ժամանակը մոտավորապես կրկնակի կլինի մեկ առաջադրանքի ժամանակից, GIL-ի պատճառով

# I/O-կապված առաջադրանքի օրինակ, որտեղ GIL-ը քիչ է նկատելի
import threading
import time

def io_bound_task():
    time.sleep(1) # I/O սպասման իմիտացիա

# Երկու թել ստեղծում I/O-կապված առաջադրանքի համար
t1 = threading.Thread(target=io_bound_task)
t2 = threading.Thread(target=io_bound_task)

սկիզբ = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
ավարտ = time.time()

print(f"Անցած ժամանակ թելերով (I/O-bound): {ավարտ - սկիզբ:.2f} վայրկյան") # Ժամանակը մոտ 1 վայրկյան կլինի, քանի որ թելերը ազատում են GIL-ը sleep ժամանակ