Threading ve Multiprocessing

Python’da threading ve multiprocessing, aynı anda birden fazla görevi gerçekleştirmek için kullanılan paralel programlama teknikleridir. Bu teknikler, programların performansını artırmak ve zaman alıcı işlemleri daha verimli bir şekilde yürütmek için kullanılır.

1. Threading

Threading, bir programın aynı anda birden fazla iş parçacığı (thread) kullanarak çalışmasını sağlar. İş parçacıkları, aynı süreç içinde çalışan hafif alt süreçlerdir ve belleği paylaşarak çalışırlar. Threading, özellikle I/O-bound işlemler için kullanışlıdır (örneğin, dosya okuma/yazma, ağ işlemleri).

a. Basit Bir Threading Örneği
Örnek:
import threading
import time

def bekle(sure, isim):
print(f"{isim} beklemeye başlıyor.")
time.sleep(sure)
print(f"{isim} beklemeyi bitirdi.")

# İki iş parçacığı oluşturma
t1 = threading.Thread(target=bekle, args=(2, "Thread 1"))
t2 = threading.Thread(target=bekle, args=(4, "Thread 2"))

# İş parçacıklarını başlatma
t1.start()
t2.start()

# İş parçacıklarının bitmesini bekleme
t1.join()
t2.join()

print("Ana program sona erdi.")

Bu örnekte:

  • İki ayrı iş parçacığı (t1 ve t2) oluşturulur ve aynı anda çalıştırılır.
  • t1 2 saniye, t2 ise 4 saniye boyunca bekler.
  • join() metodları, ana programın iş parçacıkları tamamlanana kadar beklemesini sağlar.

Çıktı:

Thread 1 beklemeye başlıyor.
Thread 2 beklemeye başlıyor.
Thread 1 beklemeyi bitirdi.
Thread 2 beklemeyi bitirdi.
Ana program sona erdi.
b. Threading ile Paylaşılan Veriler

Threading kullanırken, iş parçacıkları arasında veri paylaşımı yapılabilir. Ancak, bu durum veri yarışmaları (race condition) ve senkronizasyon sorunlarına yol açabilir.

Örnek:
import threading

sayac = 0
def arttir():
global sayac
for _ in range(100000):
sayac += 1

# İki iş parçacığı oluşturma
t1 = threading.Thread(target=arttir)
t2 = threading.Thread(target=arttir)

# İş parçacıklarını başlatma
t1.start()
t2.start()

# İş parçacıklarının bitmesini bekleme
t1.join()
t2.join()

print("Sayac:", sayac)

Bu örnekte:

  • İki iş parçacığı sayac değişkenini aynı anda artırır.
  • Veri yarışması nedeniyle sonuç beklenenden düşük olabilir.
c. Threading ile Senkronizasyon (Lock Kullanımı)

Veri yarışmalarını önlemek için threading.Lock() kullanılarak senkronizasyon sağlanabilir.

Örnek:
import threading

sayac = 0
lock = threading.Lock()

def arttir():
global sayac
for _ in range(100000):
with lock:
sayac += 1

# İki iş parçacığı oluşturma
t1 = threading.Thread(target=arttir)
t2 = threading.Thread(target=arttir)

# İş parçacıklarını başlatma
t1.start()
t2.start()

# İş parçacıklarının bitmesini bekleme
t1.join()
t2.join()

print("Sayac:", sayac)

Bu örnekte:

  • lock kullanılarak sayac değişkenine aynı anda sadece bir iş parçacığının erişmesine izin verilir.
  • Bu şekilde veri yarışması önlenir.

2. Multiprocessing

Multiprocessing, aynı anda birden fazla işlem (process) kullanarak çalışmayı sağlar. Her işlem kendi belleğine sahiptir ve diğer işlemlerden izole edilmiştir. Multiprocessing, özellikle CPU-bound işlemler için kullanışlıdır (örneğin, yoğun hesaplama işlemleri).

a. Basit Bir Multiprocessing Örneği
Örnek:
import multiprocessing
import time

def bekle(sure, isim):
print(f"{isim} beklemeye başlıyor.")
time.sleep(sure)
print(f"{isim} beklemeyi bitirdi.")

# İki işlem oluşturma
p1 = multiprocessing.Process(target=bekle, args=(2, "Process 1"))
p2 = multiprocessing.Process(target=bekle, args=(4, "Process 2"))

# İşlemleri başlatma
p1.start()
p2.start()

# İşlemlerin bitmesini bekleme
p1.join()
p2.join()

print("Ana program sona erdi.")

Bu örnekte:

  • İki ayrı işlem (p1 ve p2) oluşturulur ve aynı anda çalıştırılır.
  • p1 2 saniye, p2 ise 4 saniye boyunca bekler.

Çıktı:

Process 1 beklemeye başlıyor.
Process 2 beklemeye başlıyor.
Process 1 beklemeyi bitirdi.
Process 2 beklemeyi bitirdi.
Ana program sona erdi.
b. Multiprocessing ile Paylaşılan Veriler

İşlemler birbirinden bağımsız belleğe sahip olduklarından, veri paylaşımı yapmak için multiprocessing.Queue, multiprocessing.Pipe veya multiprocessing.Manager kullanılabilir.

Örnek:
import multiprocessing

def kare_al(sayi, queue):
queue.put(sayi * sayi)

queue = multiprocessing.Queue()

# İki işlem oluşturma
p1 = multiprocessing.Process(target=kare_al, args=(2, queue))
p2 = multiprocessing.Process(target=kare_al, args=(3, queue))

# İşlemleri başlatma
p1.start()
p2.start()

# İşlemlerin bitmesini bekleme
p1.join()
p2.join()

# Sonuçları alma
while not queue.empty():
print(queue.get())

Bu örnekte:

  • multiprocessing.Queue kullanılarak işlemler arasında veri paylaşımı yapılır.
  • Her işlem, queue nesnesine sonuçlarını ekler.
c. Multiprocessing ile Havuz (Pool) Kullanımı

Multiprocessing havuzu, bir grup iş parçacığını veya işlemi verimli bir şekilde yönetir ve aynı anda birden fazla görevi yürütür.

Örnek:
import multiprocessing

def kare_al(sayi):
return sayi * sayi

if __name__ == "__main__":
pool = multiprocessing.Pool(processes=4)
sonuc = pool.map(kare_al, [1, 2, 3, 4, 5])
print(sonuc)

Bu örnekte:

  • multiprocessing.Pool kullanılarak aynı anda birden fazla işlem yürütülür.
  • pool.map fonksiyonu, listedeki her sayı için kare_al fonksiyonunu paralel olarak çalıştırır.

Çıktı:

[1, 4, 9, 16, 25]

3. Threading ve Multiprocessing Arasındaki Farklar

  • Threading: Aynı işlem içinde hafif alt süreçlerdir. İş parçacıkları bellek paylaşır ve iletişimleri kolaydır. Ancak, GIL (Global Interpreter Lock) nedeniyle CPU-bound işlemler için sınırlı verim sunar.
  • Multiprocessing: Bağımsız işlemler olarak çalışır ve her işlem kendi belleğini kullanır. Bu, CPU-bound işlemler için daha verimlidir, ancak iletişim için ekstra araçlar gerektirir (örneğin, queue, pipe).

4. GIL (Global Interpreter Lock)

Python’da GIL (Global Interpreter Lock), aynı anda sadece bir iş parçacığının Python bayt kodunu yürütmesine izin veren bir mekanizmadır. Bu, threading kullanımında performans sınırlamalarına yol açabilir, özellikle CPU-bound işlemler için. Multiprocessing, GIL’i atlayarak bu sınırlamaları ortadan kaldırır.

Threading ve multiprocessing, Python‘da paralel programlama için iki güçlü tekniktir. Threading, I/O-bound işlemler için uygunken, multiprocessing, CPU-bound işlemler için daha verimlidir. GIL nedeniyle, threading CPU-bound işlemler için sınırlı performans sunabilirken, multiprocessing GIL’in sınırlamalarını aşarak daha iyi performans sağlar. Her iki teknik de paralel ve eşzamanlı programlama için farklı senaryolarda kullanılabilir ve verimlilik sağlar.

Comments

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.