Öncelikle Yüksek Lisans öğrencim “Melike Bektaş”e teşekkürler. Bu bölümde Python ortamında multithread bir kod oluşturmayı göreceğiz.
Bir işletim sistemi üzerinde herhangi bir dil ile kodlanmış ve bir compiler (derleyici) ile derlenmiş ve daha sonra hafızaya yüklenerek işlemcide çalıştırılan programlara process denir. Kısacası bir programın çalışan hali processtir.
Threadler ise processlerin içerisinde yer alan eş zamanlı olarak çalışabilen iş parçacıklarıdır. Yani threadler sayesinde kodlarımızı ardaşıl olarak yürütmek yerine eş zamanlı olarak yürütebiliriz.
Multithreading, çoğu yazılım dili tarafından desteklenen temel programlama prensibidir ve bir programda aynı zamanda birden fazla işin yapılabilmesini sağlamaktadır. Yani bir kod parçası bir işlemi gerçekleştirirken aynı anda ona paralel olarak bir başka kod parçasının da çalışması demektir. Multithread yapısında olmayan programlar, main thread isimli tek bir thread üzerinden yürütülürler.
Thread kullanmak;
- Kaynak paylaşımı
- Daha az bekleme süresi
- Daha verimli donanım kullanımı
gibi faydalar sağlamaktadır.
1. Multithreading Modülü
Python dilini kullanarak threadler ile ilgili uygulamalar geliştirmek istiyorsak aşağıdaki iki modülden birisini import etmemiz gerekmektedir. Bu modüller;
- <thread>
- <threading>
modülleridir.
<thread> modülü python 3 ile birlikte <_thread> olarak değişmiştir. <_thread> ve <threading> modülleri arasındaki temel fark <threading> modülünün nesne dayalı programlama prensibini desteklemesidir.
_thread modülünü kullanarak;
1 2 3 4 5 6 7 8 9 10 11 |
import _thread #_thread modülünü import ettik def f(threadName): #threadin hedefi olan fonksiyon i =0 while i<7: print(threadName ,"çalışıyor") i=i+1 _thread.start_new_thread(f,("thread-1",)) #thread tanımladık ve aynı zamanda threadi start metodu ile çalıştırdık _thread.start_new_thread(f,("thread-2",)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
thread-1thread-2 çalışıyor çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-2 çalışıyor thread-2 çalışıyor |
Yukarıda yer alan kodda thread ile ilgili fonksiyonları kullanabilmek için ilk önce _thread modülünü import ettik.
_thread.start_new_thread(f,(“thread-1”,)) ile yeni bir thread tanımladık ve threade hedef olarak f fonksiyonunu parametre olarak “thread-1”i verdik ve start ile çalıştırdık. Fonksiyon ile çalışan threadi 7 kere ekrana yazdırdık.
threading modülünü kullanarak;
1 2 3 4 5 6 7 8 9 10 11 12 |
import threading #threading modülünü import ettik def f(threadName): i =0 while i<7: print(threadName ,"çalışıyor") i=i+1 t1 = threading.Thread(target=f,args = ("thread-1", )) #threadi tanımladık ve f fonksiyonunu hedef gösterdik t1.start() #threadi çalıştırdık t2 = threading.Thread(target=f,args = ("thread-2", )) t2.start() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
thread-1 çalışıyor thread-1 çalışıyor thread-1 thread-2 çalışıyor çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-2 çalışıyor thread-1 çalışıyor thread-1 çalışıyor thread-2 çalışıyor |
Yukarıdaki kodda ilk olarak threading modülünü import ettik.
t1 = threading.Thread(target=f,args = (“thread-1”, )) kodu ile t1 adında bir thread oluşturduk, threade hedef olarak f fonksiyonunu parametre olarak da “thread-1”i verdik. Start() komutuyla threadi çalıştırdık. İki threadi de ekrana 7 kere yazdırdık. Yukarıdaki uygulamalarda threadlerin eş zamanlı olarak çalıştıkları, thread-1 bitmeden thread-2’ninde başladığı görülmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import _thread import time # threadin yapması gereken isleri fonksiyonun içerisinde tanimliyoruz def print_time( threadName, beklemeSuresi): count = 0 while count < 5: time.sleep(beklemeSuresi) count += 1 print ("%s: %s" % ( threadName, time.ctime(time.time()) )) # İki thread olusturduk ve çalistirdik try: _thread.start_new_thread( print_time, ("Thread-1", 1,) ) _thread.start_new_thread( print_time, ("Thread-2", 3,) ) except: print ("Error : thread çalışmadı") |
1 2 3 4 5 6 7 8 9 10 |
Thread-1: Sun Nov 11 20:42:56 2018 Thread-1: Sun Nov 11 20:42:57 2018 Thread-2: Sun Nov 11 20:42:58 2018 Thread-1: Sun Nov 11 20:42:58 2018 Thread-1: Sun Nov 11 20:42:59 2018 Thread-1: Sun Nov 11 20:43:00 2018 Thread-2: Sun Nov 11 20:43:01 2018 Thread-2: Sun Nov 11 20:43:04 2018 Thread-2: Sun Nov 11 20:43:07 2018 Thread-2: Sun Nov 11 20:43:10 2018 |
Yukarıda yer alan kodda ilk olarak _thread ve date modülünü import ettik. İlgili modülleri import ettikten sonra try bloğu içerisinde iki tane thread tanımladık, tanımladığımız threadlere hedef olarak print_time fonksiyonunu (threadin yapacağı iş) parametre olarak da “thread-1” ve 1’i verip çalıştırdık. Ekran çıktısı yukarıdaki gibidir. Thrad-1 1 saniye bekleme süresi ile ekrana tarihi ve sistemin saatini yazdırmaktadır. Thread-2 ise 3 saniye bekleme süresi ile ekrana tarihi ve sistemin saatini yazdırmaktadır.
Threadler ile faktöriyel hesabı;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import _thread threadId = 1 def factorial(n): global threadId if n < 1: print ("%s: %d" % ("Thread", threadId )) threadId += 1 return 1 else: returnNumber = n * factorial( n - 1 ) # recursive fonksiyon print(str(n) + '! = ' + str(returnNumber)) return returnNumber _thread.start_new_thread(factorial,(5, )) _thread.start_new_thread(factorial,(4, )) |
1 2 3 4 5 6 7 8 9 10 11 |
Thread: 1Thread: 1 1! = 1 2! = 2 3! = 6 4! = 24 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 |
Thread’ler çok kısa olduğunda yukarıdaki gibi karışmadan çalışabilir.
2. Threading Modülünün Metotları
<threading> modülünün <_thread> modülünden nesneye dayalı programlama prensiblerini desteklemesi yönünden farklıdır. Bunun yanında <threading> modülü <_thread> modülünün tüm metodlarını içerir ve <_thread> modülüne ek bazı metodları da destekler. Bu metodlar aşağıdaki gibidir:
- 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import threading #threading modülünü import ettik def f(threadName): i =0 while i<10: #print(threadName ,"çalışıyor") i=i+1 t1 = threading.Thread(target=f,args = ("thread-1", )) #threadi tanımladık ve f fonksiyonunu hedef gösterdik t1.start() #threadi çalıştırdık print(threading.activeCount()) #çalışan thread sayısı print(threading.enumerate()) #çalışan thread listesi print(threading.main_thread()) #main thread print(threading.get_ident()) #thread tanımlayıcısı |
1 2 3 4 |
4 [<_MainThread(MainThread, started 11804)>, <Thread(Thread-4, started daemon 12212)>, <Heartbeat(Thread-5, started daemon 11940)>, <HistorySavingThread(IPythonHistorySavingThread, started 10984)>] <_MainThread(MainThread, started 11804)> 11804 |
3. Thread Sınıfı Metotları
Thread sınıfının metodları aşağıdaki gibidir.
- run() : Threadin etkinliğini yaptığı işi temsil eder.
- start() : Threadin yapacağı işi, etkinliğini başlatır. run() metodunun çalıştırılmasını sağlar.
- join([time]) : Thread sonlanana kadar bekler.
- isAlive() : Threadin hala çalışıp çalışmadığını kontrol eder. True, false değer döndürür.
- getName() : Threadin adını döndürür.
- setName() : Threadin adını set eder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import threading import time class myThread (threading.Thread): #myThread isimli sinifimiz Thread sinifindan miras aldı. def __init__(self, threadID, name, counter): #myThread isimli sinifin constructori threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Starting " + self.name) print_time(self.name, 5, self.counter) print ("Exiting " + self.name) def print_time(threadName, counter, beklemeSuresi): while counter: time.sleep(beklemeSuresi) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 thread1 = myThread(1, "Thread-1", 1) #thread tanımladık thread2 = myThread(2, "Thread-2", 2) thread1.start() #threadleri çalıştırdık thread2.start() print(thread1.isAlive()) print(thread1.getName()) thread2.setName("thread-3") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Starting Thread-1 Starting Thread-2True Thread-1 Thread-1: Mon Nov 26 09:23:22 2018 Thread-1: Mon Nov 26 09:23:23 2018 thread-3: Mon Nov 26 09:23:23 2018 Thread-1: Mon Nov 26 09:23:24 2018 Thread-1: Mon Nov 26 09:23:25 2018thread-3: Mon Nov 26 09:23:25 2018 Thread-1: Mon Nov 26 09:23:26 2018 Exiting Thread-1 thread-3: Mon Nov 26 09:23:27 2018 thread-3: Mon Nov 26 09:23:29 2018 thread-3: Mon Nov 26 09:23:31 2018 Exiting thread-3 |
Yukarıda yer alan örnekte Thread sınıfını kullandık. Oluşturmuş olduğumuz myThread isimli sınıf Thread sınıfından miras almıştır.
def __init__(self, threadID, name, counter): satırı ile bu sınıfa ait bir constructor oluşturduk ve bu constructor içerisinde ilk değer ataması gerçekleştirdik.
thread1 = myThread(1, “Thread-1”, 1) satırı ile yeni bir thread tanımladık ve bu threadi thread1.start() satırı ile çalıştırdık. Start() metodu ile run() metodu tetiklendi ve thread asıl gerçekleştirmesi gereken işi bu şekilde gerçekleştirdi.
Ayrıca, kodun son bölümünde thread’ler çalışırken thread ismini değiştirdik.
4. Thread’lerin Senkronizasyonu
Thread senkronizasyonu kilit mekanizması sağlanır. Lock() metodu çağırılarak bir kilit oluşturulur. Bu metod threadlerin eş zamanlı olarak çalışmasını engelleyen bir mekanizmadır. Yani bir thread sonlanır ve diğer thread çalışmasına ondan sonra devam eder. Kilit acquire() fonksiyonu ile aktif hale gelir, release() fonksiyonu ile serbest bırakılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Starting " + self.name) threadLock.acquire() #threadleri senkronize etmek için kilitledik print_time(self.name, self.counter, 10) threadLock.release() # kiliti açtık def print_time(threadName, beklemeSuresi, counter): while counter: time.sleep(beklemeSuresi) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 threadLock = threading.Lock() threads = [] thread1 = myThread(1, "Thread-1", 1) #thread oluşturduk thread2 = myThread(2, "Thread-2", 2) thread1.start() # threadleri çalıştırmaya başladık thread2.start() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Starting Thread-1 Starting Thread-2 Thread-1: Mon Nov 12 15:24:17 2018 Thread-1: Mon Nov 12 15:24:18 2018 Thread-1: Mon Nov 12 15:24:19 2018 Thread-1: Mon Nov 12 15:24:20 2018 Thread-1: Mon Nov 12 15:24:21 2018 Thread-1: Mon Nov 12 15:24:22 2018 Thread-1: Mon Nov 12 15:24:23 2018 Thread-1: Mon Nov 12 15:24:24 2018 Thread-1: Mon Nov 12 15:24:25 2018 Thread-1: Mon Nov 12 15:24:26 2018 Thread-2: Mon Nov 12 15:24:28 2018 Thread-2: Mon Nov 12 15:24:30 2018 Thread-2: Mon Nov 12 15:24:32 2018 Thread-2: Mon Nov 12 15:24:34 2018 Thread-2: Mon Nov 12 15:24:36 2018 Thread-2: Mon Nov 12 15:24:38 2018 Thread-2: Mon Nov 12 15:24:40 2018 Thread-2: Mon Nov 12 15:24:42 2018 Thread-2: Mon Nov 12 15:24:44 2018 Thread-2: Mon Nov 12 15:24:46 2018 |
Diğer sayfada bulunan ekran çıktısında da görüldüğü gibi thread-1 bittikten sonra thread-2 çalışmış ve sona ermiştir. Senkronizasyon işlemi başarı ile sona ermiştir.
Kaynaklar:
- “Python For Learners” 114 – 125