Python İş Parçacığı: Bir Giriş
Yayınlanan: 2022-10-25Bu öğreticide, Python'daki çoklu iş parçacığı özelliklerini keşfetmek için Python'un yerleşik iş parçacığı modülünü nasıl kullanacağınızı öğreneceksiniz.
İşlemlerin ve iş parçacıklarının temellerinden başlayarak, eşzamanlılık ve paralellik kavramlarını anlarken Python'da çoklu iş parçacığının nasıl çalıştığını öğreneceksiniz. Daha sonra yerleşik threading
modülünü kullanarak Python'da bir veya daha fazla iş parçacığını nasıl başlatacağınızı ve çalıştıracağınızı öğreneceksiniz.
Başlayalım.
Süreçler ve Konular: Farklar Nelerdir?
Süreç Nedir?
İşlem , çalışması gereken bir programın herhangi bir örneğidir.
Herhangi bir şey olabilir – bir Python betiği veya Chrome gibi bir web tarayıcısından bir video konferans uygulamasına. Makinenizde Görev Yöneticisini başlatır ve Performans –> CPU'ya giderseniz, şu anda CPU çekirdeklerinizde çalışmakta olan süreçleri ve iş parçacıklarını görebileceksiniz.

Süreçleri ve Konuları Anlama
Dahili olarak, bir işlem, işleme karşılık gelen kodu ve verileri depolayan özel bir belleğe sahiptir.
Bir işlem bir veya daha fazla iş parçacığından oluşur. Bir iş parçacığı, işletim sisteminin yürütebileceği en küçük talimat dizisidir ve yürütme akışını temsil eder.
Her iş parçacığının kendi yığını ve kayıtları vardır, ancak ayrılmış bir belleği yoktur. Bir işlemle ilişkili tüm iş parçacıkları verilere erişebilir. Bu nedenle, veri ve bellek, bir işlemin tüm iş parçacıkları tarafından paylaşılır.

N çekirdekli bir CPU'da, N süreç aynı anda paralel olarak yürütülebilir. Ancak, aynı işlemin iki iş parçacığı asla paralel olarak yürütülemez, ancak eşzamanlı olarak yürütülebilir. Bir sonraki bölümde eşzamanlılık ve paralellik kavramını ele alacağız.
Şimdiye kadar öğrendiklerimize dayanarak, bir süreç ile bir iş parçacığı arasındaki farkları özetleyelim.
Özellik | İşlem | İplik |
Hafıza | Adanmış hafıza | Paylaşılan hafıza |
Yürütme modu | paralel, eşzamanlı | Eşzamanlı; ama paralel değil |
Yürütme tarafından işlenir | İşletim sistemi | CPython Tercümanı |
Python'da çoklu kullanım
Python'da Global Yorumlayıcı Kilidi (GIL), herhangi bir zamanda yalnızca bir iş parçacığının kilidi almasını ve çalışmasını sağlar. Tüm iş parçacıklarının çalışması için bu kilidi edinmesi gerekir. Bu, herhangi bir zamanda yalnızca tek bir iş parçacığının yürütülebilmesini sağlar ve eşzamanlı çoklu iş parçacığını önler.
Örneğin, aynı işlemin iki iş parçacığını, t1
ve t2
düşünün. t1
belirli bir k
değerini okurken iş parçacıkları aynı verileri paylaştığından, t2
aynı k
değerini değiştirebilir. Bu, kilitlenmelere ve istenmeyen sonuçlara yol açabilir. Ancak iş parçacıklarından yalnızca biri herhangi bir durumda kilidi alabilir ve çalıştırabilir. Bu nedenle GIL ayrıca iplik güvenliğini de sağlar.
Peki Python'da çoklu kullanım yeteneklerini nasıl elde ederiz? Bunu anlamak için eşzamanlılık ve paralellik kavramlarını tartışalım.
Eşzamanlılık ve Paralellik: Genel Bir Bakış
Birden fazla çekirdeğe sahip bir CPU düşünün. Aşağıdaki çizimde CPU'nun dört çekirdeği vardır. Bu, herhangi bir anda paralel olarak çalışan dört farklı işlemimiz olabileceği anlamına gelir.
Dört işlem varsa, işlemlerin her biri dört çekirdeğin her birinde bağımsız ve aynı anda çalışabilir. Her işlemin iki iş parçacığı olduğunu varsayalım.

İş parçacığı oluşturmanın nasıl çalıştığını anlamak için çok çekirdekli işlemci mimarisinden tek çekirdekli işlemci mimarisine geçelim. Belirtildiği gibi, belirli bir yürütme örneğinde yalnızca tek bir iş parçacığı etkin olabilir; ancak işlemci çekirdeği iş parçacıkları arasında geçiş yapabilir.

Örneğin, G/Ç'ye bağlı iş parçacıkları genellikle G/Ç işlemlerini bekler: kullanıcı girdisinde okuma, veritabanı okumaları ve dosya işlemleri. Bu bekleme süresi boyunca, diğer iş parçacığının çalışabilmesi için kilidi serbest bırakabilir . Bekleme süresi, n
saniye uyumak gibi basit bir işlem de olabilir.
Özetle: Bekleme işlemleri sırasında, iş parçacığı kilidi serbest bırakarak işlemci çekirdeğinin başka bir iş parçacığına geçmesini sağlar. Bekleme süresi tamamlandıktan sonra önceki iş parçacığı yürütmeye devam eder. İşlemci çekirdeğinin aynı anda iş parçacıkları arasında geçiş yaptığı bu işlem, çoklu iş parçacığını kolaylaştırır.
Uygulamanızda süreç düzeyinde paralellik uygulamak istiyorsanız, bunun yerine çoklu işlemeyi kullanmayı düşünün.
Python Threading Modülü: İlk Adımlar
Python, Python betiğine aktarabileceğiniz bir threading
modülüyle birlikte gelir.
import threading
Python'da bir thread nesnesi oluşturmak için Thread
yapıcısını kullanabilirsiniz: threading.Thread(...)
. Bu, çoğu iş parçacığı uygulaması için yeterli olan genel sözdizimidir:
threading.Thread(target=...,args=...)
Burada,
-
target
, çağrılabilir bir Python'u gösteren anahtar kelime argümanıdır. -
args
, hedefin aldığı argümanlar dizisidir.
Bu öğreticideki kod örneklerini çalıştırmak için Python 3.x'e ihtiyacınız olacak. Kodu indirin ve devam edin.
Python'da Konular Nasıl Tanımlanır ve Çalıştırılır
Hedef işlevi çalıştıran bir iş parçacığı tanımlayalım.
Hedef işlev some_func
.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() print(threading.active_count())
Yukarıdaki kod parçacığının ne yaptığını ayrıştıralım:
- İş
threading
vetime
modüllerini içe aktarır. -
some_func
işlevi, açıklayıcıprint()
ifadelerine sahiptir ve iki saniyelik bir uyku işlemi içerir:time.sleep(n)
, işlevinn
saniye boyunca uyumasına neden olur. - Ardından, hedefi
thread_1
olan birsome_func
iş parçacığı tanımlarız.threading.Thread(target=...)
bir thread nesnesi yaratır. - Not : Bir işlev çağrısı değil, işlevin adını belirtin;
some_func
kullanın vesome_func()
değil . - Bir iş parçacığı nesnesi oluşturmak, bir iş parçacığı başlatmaz; thread nesnesinde
start()
yöntemini çağırmak işe yarar. - Aktif iş parçacığı sayısını almak için
active_count()
işlevini kullanırız.
Python betiği ana iş parçacığı üzerinde çalışıyor ve aktif iş parçacığı sayısı çıktıda görüldüğü gibi iki olacak şekilde some_func
işlevini çalıştırmak için başka bir iş parçacığı ( thread1
) oluşturuyoruz:

# Output Running some_func... 2 Finished running some_func.
Çıktıya daha yakından bakarsak, thread1
başlatıldığında ilk print ifadesinin çalıştığını görürüz. Ancak uyku işlemi sırasında, işlemci ana iş parçacığına geçer ve iş parçacığı1'in yürütmeyi thread1
beklemeden etkin iş parçacıklarının sayısını yazdırır.

İş Parçacıklarının Yürütülmesini Bitirmesini Bekliyor
thread1
yürütmeyi bitirmesini istiyorsanız, thread'i başlattıktan sonra bunun üzerinde join()
yöntemini çağırabilirsiniz. Bunu yapmak, ana iş parçacığına geçmeden iş parçacığı1'in yürütmeyi thread1
bekleyecektir.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() thread1.join() print(threading.active_count())
Şimdi, aktif iş parçacığı sayısını yazdırmadan önce iş thread1
yürütmeyi bitirdi. Yani sadece ana iş parçacığı çalışıyor, bu da aktif iş parçacığı sayısının bir olduğu anlamına geliyor.
# Output Running some_func... Finished running some_func. 1
Python'da Birden Çok Konu Nasıl Çalıştırılır
Ardından, iki farklı işlevi çalıştırmak için iki iş parçacığı oluşturalım.
Burada geri sayım, argüman olarak bir sayıyı alan ve o sayıdan sıfıra doğru geri count_down
bir fonksiyondur.
def count_down(n): for i in range(n,-1,-1): print(i)
Sıfırdan belirli bir sayıya kadar sayan başka bir Python işlevi olan count_up
tanımlarız.
def count_up(n): for i in range(n+1): print(i)
range()
işlevirange(start, stop, step)
sözdizimi ile kullanıldığında, bitiş noktasıstop
varsayılan olarak hariç tutulur.– Belirli bir sayıdan sıfıra geri saymak için, -1'lik bir negatif
step
değeri kullanabilir ve sıfırın dahil edilmesi içinstop
değerini -1'e ayarlayabilirsiniz.– Benzer şekilde,
n
kadar saymak içinstop
değerinin + 1
olarak ayarlamanız gerekir.start
vestep
varsayılan değerleri sırasıyla 0 ve 1 olduğundan, 0'dan n'ye kadar olan diziyi elde etmek içinrange(n + 1)
kullanabilirsiniz.
Ardından, sırasıyla count_down
ve count_up
işlevlerini çalıştırmak için thread1
ve thread2
olmak üzere iki thread tanımlarız. Her iki fonksiyon için de print
deyimleri ve sleep
işlemleri ekliyoruz.
İş parçacığı nesnelerini oluştururken, hedef işlevin argümanlarının bir tanımlama grubu olarak ( args
parametresine) belirtilmesi gerektiğine dikkat edin. Her iki işlev de ( count_down
ve count_up
) tek bir argüman alır. Değerden sonra açıkça bir virgül eklemeniz gerekir. Bu, sonraki öğeler None
olarak çıkarıldığından, argümanın hala bir demet olarak iletilmesini sağlar.
import threading import time def count_down(n): for i in range(n,-1,-1): print("Running thread1....") print(i) time.sleep(1) def count_up(n): for i in range(n+1): print("Running thread2...") print(i) time.sleep(1) thread1 = threading.Thread(target=count_down,args=(10,)) thread2 = threading.Thread(target=count_up,args=(5,)) thread1.start() thread2.start()
Çıktıda:
- count_up işlevi
count_up
üzerinde çalışır vethread2
başlayarak 5'e kadar sayar. -
count_down
işlevi,thread1
10'dan 0'a geri sayım üzerinde çalışır.
# Output Running thread1.... 10 Running thread2... 0 Running thread1.... 9 Running thread2... 1 Running thread1.... 8 Running thread2... 2 Running thread1.... 7 Running thread2... 3 Running thread1.... 6 Running thread2... 4 Running thread1.... 5 Running thread2... 5 Running thread1.... 4 Running thread1.... 3 Running thread1.... 2 Running thread1.... 1 Running thread1.... 0
Her ikisi de bir bekleme işlemi (uyku) içerdiğinden, thread1
ve thread2
alternatif olarak yürütüldüğünü görebilirsiniz. count_up
işlevi 5'e kadar saymayı bitirdiğinde, thread2
artık aktif değildir. Böylece sadece thread1
karşılık gelen çıktıyı elde ederiz.
Özetliyor
Bu öğreticide, çoklu iş parçacığını uygulamak için Python'un yerleşik iş parçacığı modülünü nasıl kullanacağınızı öğrendiniz. İşte önemli çıkarımların bir özeti:
- Thread yapıcısı, bir thread nesnesi oluşturmak için kullanılabilir. threading.Thread(target=<callable>,args=(<tuple of args>)) kullanımı , args içinde belirtilen argümanlarla çağrılabilir hedefi çalıştıran bir iş parçacığı oluşturur.
- Python programı bir ana iş parçacığı üzerinde çalışır, bu nedenle oluşturduğunuz iş parçacığı nesneleri ek iş parçacıklarıdır. Active_count() işlevini çağırabilirsiniz, herhangi bir örnekte aktif iş parçacıklarının sayısını döndürür.
- İş parçacığı nesnesinde start() yöntemini kullanarak bir iş parçacığı başlatabilir ve join() yöntemini kullanarak yürütmenin bitmesini bekleyebilirsiniz.
Bekleme sürelerini değiştirerek, farklı bir G/Ç işlemi deneyerek ve daha fazlasını yaparak ek örnekler kodlayabilirsiniz. Yaklaşan Python projelerinizde çoklu iş parçacığı uyguladığınızdan emin olun. Mutlu kodlama!