Thread Program Kapanana Kadar Devam Ederse Sorun Olur mu?

Merhaba,

bir fonksiyonum var. İçinde program çalıştığı sürece çalışan bir while döngüsü var. Bu döngü ancak program kapandığında bitiyor.

Ben bu fonksiyonu threading ile ek olarak çalıştırıyorum. Kısaca bu thread program kapandığında bitiyor mu ? Yoksa arkasında bir şeyler bırakıyor mu ?

Thread biraz karışık ve tehlikeli bir konu anladığım kadarıyla. Emin olmak için soruyorum sadece.

Örnek Kod:

def fonk():
    while True:
        print("bir şeyler")

thread_1 = Thread(target=fonk)
thread_1.start() #burada başlatıyorum fakat programı kullanıcı çarpıdan kapatana kadar çalışıyor.

Sizinle iki farklı kod paylaşıyorum. Bu iki kod arasında ne fark olduğunu bulacaksınız diye tahmin ediyorum.

Kod 1:

import tkinter as tk

from time import sleep
from threading import Thread


def fonk():
    while True:
        print("Çalışıyor...")
        sleep(1)


Thread(target=fonk).start()

root = tk.Tk()
Thread(target=root.mainloop).run()

Kod 2:

import tkinter as tk

from time import sleep
from threading import Thread


def fonk():
    while True:
        print("Çalışıyor...")
        sleep(1)


Thread(target=fonk, daemon=True).start()

root = tk.Tk()
Thread(target=root.mainloop).run()

daemon'u True olarak ayarlarsanız, ana iş parçacığı sonlandığında daemon threadler de sonlanır. Aksi halde çalışmaya devam ederler.

1 Like

Thread’ler programın bir parçasıdır. Program sona erdiğinde thread’in işi bitmişse veya daemon thread ise program kapanır. Ancak thread’in işi bitmemişse ve deamon değilse thread sonlanana kadar program kapanmaz. Program sona erdikten sonra arkaplanda thread’in çalışması mümkün değil. Program kapandığı zaman işletim sistemi bu programa ait tüm kaynakları temizler, dolayısıyla thread’leri de yok eder.

3 Likes

Cevabınız için çok teşekkür ederim hocam. İzin verirseniz başka bir sorum daha olacak.
Kullanıcının programa istediği kadar görev verebileceği bir program yazıyorum. Alarm uygulaması denebilir.

Kısaca kullanıcı istediği kadar alarm ekleyebiliyor. Ben bu alarmların her birini arka planda tek tek kontrol ederek zamanı geldiğinde çalmasını istiyorum. Sanırım bunun için kullanıcının eklediği her alarm için yeni thread eklemem gerek. Fakat bunu yapamadım. Şöyle ki:

def add_new_task(self):
    print(self.combo_box_value)
    if self.combo_box_value == "Belirlenen zamanda alarm çal":
        print("başladı")
        task = Thread(target=self.alarm_cal()) #bu kısımda alarm_cal()
        #fonksiyonu her saniye bilgisayar saatini kontrol ederek belirlenen 
        #zamana eşit olması durumunda alarmın çalmasını sağlıyor.
        #fakat bu add_new_task() fonksiyonunu birden fazla çalıştırdığımda
        #yani yeni alarm eklediğimde önceki alarmlar(threadler) iptal olup, sadece
        #en son eklenen alarm çalışıyor.
        task.start()

umarım düzgün açıklayabilmişimdir.

Kodlarınızın tamamını bilmiyorum, eğer kodlarınızın tamamını paylaşırsanız, belki daha rahat yardımcı olabiliriz. Ama önce paylaştığınız kodla ilgili dikkatimi çeken bir hususu sizinle paylaşayım.

Aşağıdaki kısımda bir düzeltme yapmak lazım.

task = Thread(target=self.alarm_cal())

Burada self.alarm_cal fonksiyonunu parantez olmadan çağırmalısınız.

direkt kod şu şekilde :

def close_program(self, name):
    while True:
        now = datetime.now()#şimdiki zamanı alıyoruz
        now_updated = datetime.strftime(now, "%X")#aldığımız zamanı 00:00:00 biçimine çeviriyoruz.


        if self.get_time() == now_updated:#şimdiki zaman kullanıcının belirlediği zamana eşit olursa
            for process in (process for process in psutil.process_iter() if process.name() == name):
                process.kill()#belirlenen programları kapat(buralar thread açısından pek önemli değil)
            break
        else:
            sleep(1)#eğer şimdiki zaman belirlenen zamana eşit değilse 
            #döngü başa döner ve tekrar kontrol eder.

def add_new_task(self):
    if self.combo_box_value == "Belirlenen zamanda bir program kapat":
        print("başladı")
        p_name = self.get_program_name()#kapatılaca programın ismi çok önemli değil şu an bizim için.
        task = Thread(target=self.close_program, daemon=True, args=(p_name,))#programı kapatmak 
        #için yeni thread oluşturuyoruz. Fakat mesela iki programı kapatmak için kullanıcı bu komutu iki 
        #kere çalıştırırsa ilk komut iptal oluyor sadece en son istediği program çalışıyor.
        task.start()

ne tür bir program olduğunu anlamanız açısından bir ekran görüntüsü:

zamanı ayarla başlığı altında kullanıcının girdiği saat değeri var. Şimdiki zaman ise sürekli çalışan bir thread ile kontrol ediliyor. Yani bilgisayar saatini gerçek zamanlı olarak gösteriyor.

Resimde kullanıcı görevi ekle butonuna tıklarsa thread oluşturuluyor ve belirlenen saatte chrome programı kapanıyor. Buraya kadar güzel. Fakat örneğin program ismine firefox.exe yazıp saat belirledikten sonra tekrar görevi ekle butonuna tıkladığında chrome’u kapatma görevi iptal oluyor ve program sadece firefox’u kapatıyor.

Demek istediğinizi anladım da, paylaştığınız kodlar sorunun nereden kaynaklandığını tespit etmek için pek yeterli gelmedi bana. Yani en azından ben tespit edemedim henüz. Thread’i sonlandıran şey onun join metodu olabilir diye geliyor aklıma. Ama kodlarınızda join metodunu göremedim. Ayrıca bir thread yapılacak işlemler bittiği zaman sona erer. Ama diyorsunuz ki kurduğunuz ilk alarm henüz çalmadan ikinci alarmı kuruyorsunuz ve ilk alarm iptal oluyor. E demek ki çalışması normalde sona ermemeli ama bir şey bu thread’i sonlandırıyor. Ya da sonlandırmıyor ama tarihlerde bir değişme oluyor olabilir belki. Bilemedim. Şuanlık sadece tahmin yürütüyorum.

Yani şunu demeye çalışıyorum, kapatılacak programın özelliklerini (kapatılacak program adı, kapatma tarihi gibi verileri) bir takım değişkenlerde mi tutuyorsunuz? Bu değişkenler her alarm kurmanızda kendi üzerilerine mi yazılıyor? Şayet böyleyse, değişkenleri bir listeye alın, eğer bu değişkenlere sonradan ihtiyacınız olursa tanımladığınız liste üzerinden onlara ulaşabilirsiniz.

şu an bir fikrim var gibi ama ne kadar mantıklı bilemiyorum.

def add_new_task(self):
    if self.combo_box_value == "Belirlenen zamanda bir program kapat":
        print("başladı")
        p_name = self.get_program_name()#kapatılaca programın ismi çok önemli değil şu an bizim için.
        task = Thread(target=self.close_program, daemon=True, args=(p_name,))#programı kapatmak 
        #için yeni thread oluşturuyoruz. Fakat mesela iki programı kapatmak için kullanıcı bu komutu iki 
        #kere çalıştırırsa ilk komut iptal oluyor sadece en son istediği program çalışıyor.
        task.start()

bu fonksiyon her çalıştığında task değişkeni tekrar tanımlanıyor. Mesela:

def deger_degistir(deger):
    a = deger
deger_degistir(5)
deger_degistir(3)

bu kodda a nın değerleri her çalıştırmada değiştiği gibi add_new_task() fonksiyonu her çalıştığında task’ın değeri değiştiği için önceki değişken(yani thread) işlevini yitiriyor olabilir mi ?

Hmm. self.get_program_name() fonksiyonuna bir bakabilir miyiz?

def get_program_name(self):
    return str(self.program_ismi.text())

bu fonksiyon arayüzdeki program ismi entry’sinin değerini döndürüyor.

hocam bu arada şu an çalışıyor fonksiyon garip bir şekilde. Konunun başında bahsettiğiniz daemon=True
değerini ekledim ve pycharm’ı yeniden başlattım şu an çalışıyor :smile: teşekkürler cevaplarınız için yordum sizi.

Paylaştığınız kodlardan sorunu tespit edemedim, aklıma gelenleri paylaştım sizinle. Çalışıyorsa sıkıntı yok. Çalışmıyorsa devam edelim. :slight_smile:

1 Like

Her alarm için yeni bir thread eklemeniz gerekmiyor, hepsini beraber de yapabilirsiniz. Biri bittiğinde bir sonraki için bekler.

Şu kod sadece iki Thread ile çalışabilmektedir, biri alarmlar için biri de arayüz veya ana thread için:

from datetime import datetime
from threading import Thread

def zamanlayıcı():
    while True:
        if len(zamanlar)==0:
            continue
        if zamanlar[0].datetime < datetime.now():
            fonk = zamanlar.pop(0)
            fonk.işlem()

class İşlem:
    def __init__(self,işlem,datetime):
        self.işlem = işlem
        self.datetime = datetime

    def __repr__(self):
        return str(self.datetime)

zamanlar = []

Thread(target=zamanlayıcı).start()

def işlem_ekle(zaman : datetime,işlem=lambda:None):
    if zaman < datetime.now(): return False
    sıra = 0
    try:
        while zaman > zamanlar[sıra].datetime:
            sıra += 1
    except IndexError:
        zamanlar.append(İşlem(işlem,zaman))
    else:
        zamanlar.insert(sıra,İşlem(işlem,zaman))
    
a = datetime(2019,11,26,23,7,20)
b = datetime(2019,11,26,23,7,10)
c = datetime(2019,11,26,23,7)
işlem_ekle(b,lambda:print("b"))
işlem_ekle(c,lambda:print("c"))
işlem_ekle(a,lambda:print("a"))

Kullanma şekli şu şekildedir. Tek yapmanız gereken işlem_ekle fonksiyonuna sırasıyla
işlemin çalışacağı zamanı ve yapacağı işi fonksiyon olarak vermek. İşlemler çalışma sırası ile zamanlar adlı listede saklanacak. Çalışması bittikten sonra silinecek. zamanlar listesinden bir işlem silmenizde sakınca yoktur. Bir işlemin işlem niteliğini değiştirmenizde de sakınca yoktur ancak zamanını değişterirseniz şu yerleştirme algoritmasını tekrar kullanmanız gerekir:

def işlem_zamanı_değiştir(index : int,zaman : datetime):
    işlem = zamanlar.pop(index)
    if zaman < datetime.now(): return False
    işlem.datetime = zaman
    sıra = 0
    try:
        while zaman > zamanlar[sıra].datetime:
            sıra += 1
    except IndexError:
        zamanlar.append(işlem)
    else:
        zamanlar.insert(sıra,işlem)

Burada da index işlemin zamanlar listesindeki indexi, zaman ise yeni zaman değeri.