Python Thread ve Process Kullanimi 1

Thread ve Process kullanımını öğrenmeden önce thread ve process’in anlamlarını ve işlevlerini bilmemiz gerekir.

Process (işlem)

Tanım olarak herhangi bir dil ile kodlanmış ve bir derleyici ile derlenmiş ve daha sonra hafızaya yüklenerek işlemcide çalıştırılan programlara verilen isimdir.

Fakat python yorumlamaya dayalı bir dil olduğu için kısaca process bir programın, yazılımın çalısan halidir. Bir programın, yazılımın birden fazla process’i olabilir. Her process kendi adres alanında çalışır.

Thread (İplik)

Threadler processlerin içerisinde yer alan eş zamanlı olarak çalışabilen iş parçacıklarıdır. Bir process içinde birden fazla thread olabilir.

Bir process başlatıldığında bir thread içerir. Bu threadler yazılıma göre coğaltılarak eş zamanlı olarak işlem gercekleştirilmiş olur.

Single Threading Multi-Threading.

Python Threading

Python’da thread kullanabilmemiz için birden fazla kütüphane var. İlk olarak threading kütüphanesi ile başlayalım.

İlk olarak threading yapısını uygulayacağımız fonksiyonu inceleyelim.

import time


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")


start_time = time.perf_counter()
for i in range(3):
    wait_func()
end_time = time.perf_counter()

print(f"Finished in ==> {round(end_time-start_time, 2)} seconds")

wait_func -> default olarak 5 saniye bekleyen bir fonksiyon ve bu fonksiyonu for içinde 3 kere çağırıyoruz.

wait code output

Çıktımızda da gördüğümüz gibi fonksiyon sırayla çağrılıyor ve bitmesi 15 saniye sürüyor.

Gelin bu fonsiyonu thread ile çağıralım.

import time
from threading import Thread
start_time = time.perf_counter()


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")


threads = []
for i in range(3):
    thread_instance = Thread(target=wait_func, args=(5,))
    thread_instance.start()
    threads.append(thread_instance)

for thread in threads:
    thread.join()
end_time = time.perf_counter()
print(f"Finished in ==> {round(end_time-start_time, 2)}")

wait_func’da değişiklik yapmadık fakat fonksiyonu thread ile çağırdık.

for i in range(3):    
    thread_instance = Thread(target=wait_func, args=(5,))    
    thread_instance.start()    
    threads.append(thread_instance)

Thread içerisinde target ile çalıstırmak istediğimiz fonksiyonu belirtiyoruz ve arguman vermek istersek args=(x,) ile verebiliriz. Bizim durumumuzda pek gerekli olmasa da akıllardaki soru işaretini gidermek için yazdım.

İsterseniz args seçeneğini array olarak da geçebilirsiniz( args=[5] )

Thread Object’imizi oluşturduktan sonra bunu başlatmamız lazım. Bu işlemi thread_instance.start() fonksiyonu ile yapıyoruz.

Thread Object’leri kaybetmemek için onlari ‘threads’ adında bir Array’e atıyoruz.

Thread’ler başlatıldığında ana program çalışmaya devam eder. Yani bu threadlerden gelecek bir veriyi veya yaptığı işlemi yaptıktan sonra devam etmek istersek Thread Object’lerini ana Thread’e join etmeliyiz yani birleştirmeliyiz. Join, Thread’lerin işlemleri bitene kadar onları bekler ve bittiğinde işlemlerine devam eder.

for thread in threads:    
    thread.join()

Buradaki join işleminin amacı oluşturulan Threadler’i beklemek ve daha sonra geri kalan kodları çalıstırmaktır.

Şimdi çıkan sonucumuzu inceleyelim.

wait code with thread

Sonucumuzdan da görüldüğü gibi Threadler eş zamanlı olarak başladı ve 5 saniye beklediler. Sonuç olarak 15 saniyede çalışan programımız 5 saniye gibi kısa bir süreye indi.

Join Fonksiyonunun Önemi

for thread in threads:    
    thread.join()

Kodumuzdan yukarıdaki kısmi çıkararak join fonksiyonunun ne anlama geldiğini daha iyi anlayabiliriz.

import time
from threading import Thread
start_time = time.perf_counter()


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")


threads = []
for i in range(3):
    thread_instance = Thread(target=wait_func, args=(5,))
    thread_instance.start()
    threads.append(thread_instance)

end_time = time.perf_counter()
print(f"Finished in ==> {round(end_time-start_time, 2)}")

Kodumuzu çalıstırdığımızda kodun anında bittiğini göreceksiniz fakat sonrasında beklemeye devam ediyor. Bunun nedeni threadlerin bitmesini beklemeden kodlarımiz çalıştırıldı.

Join fonksiyonunun yeri yazdığınız koda gore değişkenlik gösterebilir, performansa etki eden etmenlerdendir.

İstediğiniz şey main thread’in işi bittiğinde diğerlerinin otamatik olarak sonlaması ise napmalısınız ?

Hadi gelin bunu inceleyelim.

Daemon Kavrami

Python kodunuzu çalıştırdığınızda main thead oluşur ve oluşturduğunuz diğer threadler bu thread üzerinden oluşur. Eğer işlemler biterse bu thread sonlandırılır. Thread oluşturduysanız main thread o threadlerin işlemlerini bitirmesini bekler (join tanımlamazsanız kendi işlemleri sonunda bekler).

Oluşturduğunuz threadleri main thread’in işi bittiğinde otomatik olarak kapatmak isterseniz daemon özelliğini kullanabilirsiniz.

import time
from threading import Thread
start_time = time.perf_counter()


def wait_func():
    print(f"will sleep 10 second is working")
    time.sleep(10)
    print("slept 10 second")


thread_instance = Thread(target=wait_func)
thread_instance.daemon = True

thread_instance.start()

time.sleep(3)

end_time = time.perf_counter()

print(f"Finished in ==> {round(end_time-start_time, 2)} seconds")

Burada daemon’u Thread fonksiyonu içinde option olarak da verebiliriz.

Genel olarak kodumuz 10 saniye bekliyor fakat ana program threadi oluşturup 3 saniye bekleyerek programı sonlandırıyor. Yani “slept 10 second” yazısını çıktımızda görmeyeceğiz.

thread daemon true

threading Modülünün Sunduğu Bazı Fonksiyonlar

  • threading.activeCount() : Aktif olarak çalışan thread sayısını geri döndürür.
  • threading.enumerate() : Aktif olarak çalışan threadlerin listesini geri döndürür.
  • threading.main_thread() : Main threadi geri döndürür.
  • threading.get_ident() : Threadin tanımlayıcısını (identifier) geri döndürür.

Thread kullanirken karşılaşabileceğiniz problemlerden belki de en önemlisi fonksiyondan dönecek değerleri almaktır.

Bunun için en uygun yol kendinize ait bir sınıf oluşturmak ve bu sınıf aracılığıyla bilgilerini tutmaktır. Bu sayede bilgileri ve methodları kendiniz ayarlayabilirsiniz. Bununla birlikte Python queue adında bir modül sunmaktadir. Bu da işinizi görecektir.

Threading kütüphanesinin sunduğu thread yapısının fonksiyondan dönen değeri alamadığını anlamış olduk. Peki dönen değeri almanın bir yolu var mı ?

ThreadPoolExecutor

concurrent.futures kütüphanesi Python’in bir diğer thread ve process kütüphanesidir.

Örneğimiz üzerinden nasıl kullanıldığını öğrenelim

from concurrent.futures import ThreadPoolExecutor
import time

start_time = time.perf_counter()


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")


with ThreadPoolExecutor() as executor:
    for i in range(3):
        thread = executor.submit(wait_func, 5)

end_time = time.perf_counter()
print(f"Finished in ==> {round(end_time-start_time, 2)}")

submit fonksiyonu kolayca thread oluşturmamıza olanak sağlıyor.

With sayesinde otomatik olarak threadler sonlandığında devam ediliyor. Thread oluşturulup bitmesini beklemeden devam edilmesini isterseniz normal obje oluşturup daha sonra object_name.done() ile kontrol sağlayabilirsiniz.

Bir diğer kolay kullanım yolu ise for’a gerek kalmadan listedeki her item için thread oluşturma.

from concurrent.futures import ThreadPoolExecutor
import time

start_time = time.perf_counter()


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")


with ThreadPoolExecutor() as executor:
    array = [5, 2, 3, 1]
    thread = executor.map(wait_func, array)


end_time = time.perf_counter()
print(f"Finished in ==> {round(end_time-start_time, 2)}")

Map fonksiyonu sayesinde kolayca threadler oluşturabiliyoruz.

Peki fonksiyonun döndürdüğü bir değeri nasıl alacağız ?

from concurrent.futures import ThreadPoolExecutor
import time

start_time = time.perf_counter()


def wait_func(s=5):
    print(f"will sleep {s} second")
    time.sleep(s)
    print(f"slept {s} second")
    return f"slept {s} second"


with ThreadPoolExecutor() as executor:
    thread = executor.submit(wait_func, 5)
    print(f"result comes from func is ==> {thread.result()}")


end_time = time.perf_counter()
print(f"Finished in ==> {round(end_time-start_time, 2)}")

Fonksiyondan gelen değeri result thread objesinin result fonksiyonu ile kolayca alabiliyoruz.

Map fonksiyonunda dönen değerler dizi şeklinde gelir.

Kodları aşağıdan indirebilirsiniz.

Yararlı Olabilecek Kaynaklar:

https://www.youtube.com/watch?v=IEEhzQoKtQU

https://kerteriz.net/python-multithreading-programlama/

Tags:
2 Comments

Add a Comment

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