Kurs
Python'a yeni başlıyorsanız ve daha fazla öğrenmek istiyorsanız, DataCamp'in Python ile Veri Bilimine Giriş kursunu alın.
Küresel yorumlayıcı kilidi (GIL), bir yorumlayıcıya küresel bir kilit uygulamak için kullanılan bir mekanizmadır. İşletim sistemi tarafından zamanlanan yalnızca bir yerel iş parçacığının aynı anda çalışabilmesini sağlamak için iş parçacıklarının yürütülmesini senkronize edip yönetmek amacıyla programlama dili yorumlayıcılarında kullanılır.
Birden fazla iş parçacığınızın olduğu bir senaryoda, her iki iş parçacığı da aynı anda belleği edinmeye çalışabilir ve bunun sonucunda bellekteki verilerin üzerine yazabilir. Bu nedenle, bu olguyu önlemeye yardımcı olacak bir mekanizmaya ihtiyaç doğar.
GIL'e sahip bazı popüler yorumlayıcılar CPython ve Ruby MRI'dir. Çoğunuzun bildiği gibi Python bir yorumlanan dildir; CPython, Jython, IronPython gibi çeşitli dağıtımları vardır. Bunlardan yalnızca CPython GIL'i destekler ve Python'un en yaygın kullanılan uygulamasıdır. CPython, başta C diliyle yoğun şekilde çalışan uygulamaları desteklemek ve onlarla çalışmak üzere hem C hem de Python dillerinde geliştirilmiştir.
İşlemcinizde birden çok çekirdek olsa bile, küresel bir yorumlayıcı aynı anda yalnızca bir iş parçacığının çalışmasına izin verir. Bunun nedeni, bir iş parçacığı çalışmaya başladığında küresel yorumlayıcı kilidini edinmesidir. Herhangi bir G/Ç işlemi (diskten okuma/yazma) veya CPU ağırlıklı bir işlem (vektör/matris çarpımı) beklediğinde kilidi serbest bırakır ki böylece o işlemin diğer iş parçacıkları çalışabilsin. Dolayısıyla, diğer iş parçacıklarını aynı anda çalıştırmanızı engeller.

Yukarıdaki diyagramı bir an için inceleyelim. Görüldüğü gibi bir factorial fonksiyonu ve 1 ile 2 numaralı iki iş parçacığı var; iş parçacığı 1 kilitli durumda, iş parçacığı 2 ise bekleme durumunda. Bu, iş parçacıklarından yalnızca birinin fonksiyona erişebildiği anlamına gelir. Şimdi, factorial fonksiyonunun tamamlanması 2 saniye sürüyor varsayalım. İdeal durumda, her iki iş parçacığının da yürütmeyi 2 saniyede bitirebilmesi gerekir. Ancak Python'da durum böyle değildir; iki iş parçacığı birbirine paralel değil, seri olarak çalışacaktır.
factorial fonksiyonunu çağıran 1 ve 2 numaralı iş parçacıkları, fonksiyonu iki kez çağıran tek bir iş parçacığına kıyasla iki kat daha uzun sürebilir. Bu da yorumlayıcı tarafından yönetilen bellek yöneticisinin iş parçacığı güvenli olmadığını, yani birden fazla iş parçacığının aynı paylaşılan veriye eşzamanlı olarak erişemediğini gösterir.
Bu nedenle, GIL (Kaynak: Understanding the Python GIL):
-
İş parçacığı (threading) işlemlerini sınırlar.
-
Paralel yürütmeyi kısıtlar.
-
GIL, yorumlayıcıda aynı anda yalnızca bir iş parçacığının çalışmasını sağlar.
-
Bellek yönetimi gibi çeşitli düşük seviye ayrıntıları basitleştirmeye yardımcı olur.
GIL ile paralel hesaplama yerine eşgüdümlü çoklu görev ya da başka bir deyişle işbirlikçi (cooperative) hesaplama gerçekleştirilir.
Yukarıdaki diyagramdan görülebileceği üzere, üç iş parçacığı vardır; başlangıçta iş parçacığı 1 çalışmaktadır ve GIL'i edinmiştir; okuma, yazma vb. bir G/Ç işlemi yapıldığında iş parçacığı 1 GIL'i serbest bırakır ve ardından iş parçacığı 2 tarafından edinilir. Bu döngü böyle devam eder ve GIL, iş parçacıkları programın yürütmesini tamamlayana kadar sırayla iş parçacıkları arasında el değiştirir. Kilidi elinde bulundurmayan ve yürütmeyi tamamlamamış herhangi bir iş parçacığının bekleme durumunda olacağını unutmayın.
Python2 ve Python3'te GIL

Python 2.7'deki Küresel Yorumlayıcı Kilidi, Python3'le karşılaştırıldığında farklı çalışır. Saf bir CPU ağırlıklı işlemde, G/Ç işlemi olmadığı için iş parçacığı çalışmaya devam eder; bu durumda diğer iş parçacıkları boşta ya da bekleme durumunda kalır ki bu istenen bir durum değildir. Bu sorunu çözmek için Python2'de tick olarak bilinen bir kavram vardır. Küresel yorumlayıcı kilidi, iş parçacığının durumu olan bekleme durumu, G/Ç işlemi ya da çalışıp çalışmadığını izlemek için bir kontrol gerçekleştirir. Ancak GIL iş parçacıklarını her an izlemeye devam etmez; bunun yerine tick kavramını kullanır ve her 100 tick'te bir iş parçacıklarını kontrol eder.
Bir tick bir bayt kodu talimatıdır ve 100 bayt kodu talimatı tamamlandığında GIL, iş parçacıklarının çalışıp çalışmadığını veya bekleme durumunda olup olmadığını kontrol eder. Bir tick'in zamanla ilişkili olmadığını, çünkü bir bayt kodu talimatı olduğunu unutmamak önemlidir. Her bir tick diğerine kıyasla daha uzun ya da daha kısa sürede çalışabilir.
Her 100 tick'ten sonra iş parçacıklarını periyodik olarak izleme kontrolü özellikle CPU ağırlıklı işlemlerde gereklidir; zira bu tür işlemlerde herhangi bir G/Ç işlemi yoktur. Yerleşik Python sys modülünü kullanarak tick sayacını değiştirebileceğinizi unutmayın.
Yukarıdaki yaklaşımın bazı sakıncalarını tartışalım:
Şu diyagrama bakarsanız, iş parçacıklarının birbirine paralel değil, seri düzende çalıştığını fark edebilirsiniz; bu da küresel yorumlayıcı kilidi tarafından yönetilen işbirlikçi hesaplama olarak bilinir. Ayrıca iş parçacıklarının daha uzun süreler beklemesi gerekir. Örneğin T3 iş parçacığı, GIL'i serbest bırakmaları için hem T1 hem de T2 iş parçacıklarını beklemek zorunda kalmıştır. Bu nedenle iş parçacıkları kilit ve işlem için adeta bir kıtlık yaşar.
Bir başka sakınca da, GIL'i hangi iş parçacığının edineceği konusunda iş parçacıkları arasında bir rekabet olmasıdır. Başlangıçta önceliğe bağlı olarak önce T1, sonra T2 vb. GIL'i edinmiş olabilir. Ancak T3 GIL'i serbest bıraktığında tüm diğer iş parçacıklarına bir bildirim gönderecektir; bu durumda tüm iş parçacıkları GIL'i edinmek için mücadele edecektir. Bununla birlikte, her iş parçacığının önceliklerini ayarlayarak bunu belli ölçüde aşabilirsiniz.
Python3'te her iş parçacığı için sabit bir süre ayrılmıştır; örneğin 5 ms yürütme süresi. Bu, bekleme süresi tüm iş parçacıkları için eşit olduğundan kaynakların yetersizliğinden dolayı iş parçacıklarının mahrum kalmasını önler. İş parçacıkları, GIL'in İş Parçacığı 1 tarafından G/Ç ya da uyku işlemi nedeniyle kasıtlı olarak serbest bırakılıp bırakılmadığını bekleyecektir. Eğer bırakılmazsa, 5 ms sonra serbest bırakmaya zorlayacaktır. Böylece her iş parçacığı eşit zaman alacağından CPU süresi açısından bir yoksunluk yaşanmayacak ve GIL'i edinme zorluğu ortadan kalkacaktır.
Ancak, GIL'in yeni uygulamasının G/Ç işlemlerine kıyasla CPU ağırlıklı işlere daha fazla öncelik verdiğini not etmek önemlidir. Bir iş parçacığının GIL'i serbest bıraktığı ve iki iş parçacığının beklediği bir durumda, GIL diğerine kıyasla CPU ağırlıklı bir iş yürütmesi gereken iş parçacığı tarafından edinilecektir.
Sonuç
Eğitimi tamamladığınız için tebrikler.
Bu eğitim ileri düzey bir konuyu ele aldı ve amacı, GIL'in Python'da tam olarak nasıl çalıştığına dair teorik bir genel bakış sunmaktı. Bu eğitim GIL ve iş parçacıklarıyla ilgili herhangi bir kodlama yönünü kapsamadığından, sizin için iyi bir alıştırma; birden fazla iş parçacığı ve G/Ç yoğun bir görev gerçekleştiren bir yöntem oluşturup GIL'in iş parçacıkları arasında nasıl değiştiğini kendiniz deneyerek analiz etmek olacaktır.
Lütfen bu eğitimle ilgili tüm sorularınızı aşağıdaki yorumlar bölümünde sormaktan çekinmeyin.
Kaynakça:
Python'a yeni başlıyorsanız ve daha fazla öğrenmek istiyorsanız, DataCamp'in Python ile Veri Bilimine Giriş kursunu alın.
