Forum Programmation.python Threads : Help !

Posté par  (site web personnel) .
Étiquettes : aucune
1
27
oct.
2009
Bonjour,

J'essaye de jouer un peu avec les threads.

Mon but est de créer une classe avec plusieurs méthodes. Dans l'idéal, cette classe est un thread qui tourne et je lui demande de faire différentes choses à différents moments. Le tout étant qu'il ne fasse pas deux choses en même temps (lock). Je n'ai pas encore bien compris comment tout ça fonctionnait, et la doc n'est pas très explicite...

J'ai réussi à faire à peu près ce que je voulais, mais ça me parait vraiment bourrin :

#!/usr/bin/python
# -*- coding: ISO-8859-15 -*-

from threading import Thread
from time import sleep

class ComplexThread(Thread):
    def __init__(self):
       Thread.__init__(self)
       self.target = None
       self.lock = 0
    def run(self):
       while 1:
             if self.target == 'funct_1' :
                self.lock = 1
                self.funct_1()
                self.target = None
                self.lock = 0
             elif self.target == 'funct_2' :
                self.lock = 1
                self.funct_2()
                self.target = None
                self.lock = 0
             elif self.target == 'stop':
                break
       pass
    def set_target(self, target):
       self.target = target
       print target
    def funct_1(self):
       print("In function 1")
       sleep(2)
       print('finished funct_1')
    def funct_2(self):
       print('In function 2')
       sleep(2)
       print('finished funct_2')

if __name__=='__main__':
    test = ComplexThread()
    test.start()
    test.set_target('funct_1')
    sleep(0.1)
    while test.lock:
       print('.')
       sleep(0.1)
    test.set_target('funct_2')
    sleep(0.1)
    while test.lock:
       print('o')
       sleep(0.1)
    test.set_target('stop')


Merci pour votre aide !
  • # Mieux?

    Posté par  (site web personnel) . Évalué à 3.

    La méthode que j'utilise, pour éviter l'attente active comme tu le fais (le while 1 en boucle), est la suivante, qui a l'avantage d'être bloquante (l'OS sait que le processus n'a rien à faire donc utilise le CPU pour autre chose).
    #!/usr/bin/python
    # -*- coding: ISO-8859-15 -*-
    
    from threading import Thread
    from Queue import Queue
    from time import sleep
    
    
    class StopException: pass
    
    
    def thread_action(f):
        def newf(self):
            self.working = True
            f(self)
            self.working = False
        return newf
    
    
    class ComplexThread(Thread):
    
        def __init__(self):
            Thread.__init__(self)
            self.__queue = Queue()
    
        def stop(self):
            raise StopException
    
        def run(self):
            try:
                self.working = False
                while True:
                    # La clé se situe dans le fait que ce queue.get() est bloquant
                    self.__queue.get()()
            except StopException:
                # Stoping thread
                pass
    
        def set_target(self, target):
            self.__queue.put(target)
    
        @thread_action
        def funct_1(self):
            print('In function 1')
            sleep(2)
            print('finished funct_1')
    
        @thread_action
        def funct_2(self):
            print('In function 2')
            sleep(2)
            print('finished funct_2')
    
    
    if __name__=='__main__':
        test = ComplexThread()
        test.start()
        test.set_target(test.funct_1)
        sleep(0.1)
        while test.working:
           print('.')
           sleep(0.1)
        test.set_target(test.funct_2)
        sleep(0.1)
        while test.working:
           print('o')
           sleep(0.1)
        test.set_target(test.stop)
    
    Bref je te conseille de jeter un oeil à Threading.Lock(), threading.Event(), Queue.Queue(), c'est bon de savoir que ça existe. Aussi, pour commenter un peu ce que j'ai écrit, j'utilise les décorateurs (modifier plusieurs fonctions de la même manière pour changer l'attribut working), et le fait qu'en python, une fonction est un objet que l'on peut passer en paramètre. Bon courage pour la doc!
    • [^] # Re: Mieux?

      Posté par  (site web personnel) . Évalué à 1.

      Merci, ça a l'air sympa ta technique.

      Mais n'y a-t-il pas une autre manière que définir une méthode du style 'set_target' pour faire faire différentes chose par un thread ?

      La doc python est très fournie, mais pour aborder un sujet aussi complexe que les threads, un petit tuto me ferais le plus grand bien. Malheureusement, les tuto de threads que j'ai pu trouver jusqu'à maintenant ne définissent qu'une seule méthode run() appelée par start(). Aucune ne parle de changer le comportement d'un thread qui attends des instructions...

      Bon, s'il n'y en a pas (de tuto), je me poserais tranquillement devant mon bureau, étudierais le sujet avec calme et tenterais d'en faire un (de tuto). Ce sera pas perdu.
      • [^] # Re: Mieux?

        Posté par  (site web personnel) . Évalué à 2.

        Mais n'y a-t-il pas une autre manière que définir une méthode du style 'set_target' pour faire faire différentes chose par un thread ?

        Des milliers j'imagine.

        Précise.
        • [^] # Re: Mieux?

          Posté par  (site web personnel) . Évalué à 1.

          Du genre qui s'utilise comme ça :


          test = ComplexThread()
          test.start() #On démarre le thread, il attend les instruction
          test.funct_1() #Il démarre la fonction 1 en tache de fond
          test.funct_2() #Il met la fonction 2 en attente dans le thread
          sleep(4)
          test.funct_3(arg1='toto') # Il démarre la fonction 3 qui demande un argument
          test.killall() # Et s'il existe une façon d'arrêter les calculs en cours, ce serait l'idéal


          Le but étant d'appeler le thread par une interface graphique et chaque clique est un appel à une méthode de cette classe.

          L'utilisation du thread m'est utile pour ne pas freezer le soft pendant qu'il fait un calcul.

          voili voilou...

          Ça paraît simple comme ça, mais mes essais pour l'instant ne marchent pas, a part la pirouette que j'ai posté.
          • [^] # Re: Mieux?

            Posté par  (site web personnel) . Évalué à 1.

            Ce que j'ai posté fonctionne comme ça. Il suffit d'adapter pour passer un argument, et certainement un callback pour être prévenu quand c'est fini et mettre à jour l'IHM.
            • [^] # Re: Mieux?

              Posté par  (site web personnel) . Évalué à 1.

              Presque, on doit faire un test.set_target('test.funct_1') au lieu de test.funct_1().

              C'est purement cosmétique... mais ce serait plus lisible.
              • [^] # Re: Mieux?

                Posté par  . Évalué à 1.

                Tu peux changer le décorateur si tu veux ça.


                Rien ne t'empêche de faire un "self.set_target(f)" au lieu de f(self) dans ce cas.



                Pour autoriser le passage de paramètres, une possibilité est de changer le type de valeurs de la queue pour stocker un tuple (fonction, arguments) plutôt que juste une fonction. Mais il y a peut-être plus simple.


                Dans tous les cas, le code ci-dessus contient tout ce qui faut au niveau threading : le reste n'est que de la programmation "classique" à coller par-dessus.


                PS : pour le kill_all, il y a bien une fonction, mais de mémoire elle n'est pas recommandée. Je n'ai pas la doc sous les yeux donc je ne peux pas te donner le nom exact... mais elle est dans la doc de base du module threading il me semble.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.