2010-11-01 13 views
2

J'écris une application qui écoute les événements sonores (en utilisant les messages transmis avec Open Sound Control), puis en fonction de ces événements suspend ou reprend l'exécution du programme. Ma structure fonctionne la plupart du temps mais bombarde toujours la boucle principale, donc je suppose que c'est un problème de thread. Voici une version générique, simplifiée de ce dont je parle:question de structure de threads idéale (implique plusieurs communications de thread)

import time, threading 

class Loop(): 
    aborted = False 

    def __init__(self): 
     message = threading.Thread(target=self.message, args=((0),)) 
     message.start() 

     loop = threading.Thread(target=self.loop) 
     loop.start() 


    def message(self,val): 

     if val > 1: 
      if not self.aborted: 
       self.aborted = True 
       # do some socket communication    
      else: 
       self.aborted = False 
       # do some socket communication 


    def loop(self): 
     cnt = 0 

     while True: 
      print cnt 
      if self.aborted: 
       while self.aborted: 
        print "waiting" 
        time.sleep(.1); 
      cnt += 1 


class FakeListener(): 
    def __init__(self,loop): 
     self.loop = loop 
     listener = threading.Thread(target=self.listener) 
     listener.start() 

    def listener(self): 
     while True: 
      loop.message(2) 
      time.sleep(1) 



if __name__ == '__main__': 

    loop = Loop() 

    #fake listener standing in for the real OSC event listener 
    listener = FakeListener(loop) 

Bien sûr, ce code simple semble fonctionner à merveille, il est donc clair illustrant pas complètement mon code réel, mais vous voyez l'idée. Ce qui n'est pas inclus ici, c'est aussi le fait que sur chaque boucle, la pause et la reprise (en définissant aborted = True/False) aboutissent à une communication par socket qui implique également des threads.

Ce qui se passe toujours dans mon code, c'est que la boucle principale ne reprend pas toujours là où elle s'était arrêtée après un événement sonore. Cela fonctionnera pour un certain nombre d'événements, mais finalement, il ne répondra tout simplement pas.

Des suggestions pour structurer ce type de communication entre threads?

MISE À JOUR:

ok, je pense que je l'ai. voici une modification qui semble fonctionner. un thread d'écoute met périodiquement une valeur dans un objet File d'attente. il y a un thread checker qui continue de vérifier la file d'attente à la recherche de la valeur, et une fois qu'il l'a vu, il place un booléen dans son état opposé. cette valeur booléenne contrôle si le thread de boucle continue ou attend.

Je ne suis pas entièrement sûr de ce que fait la fonction q.task_done() ici.

import time, threading 
import Queue 

q = Queue.Queue(maxsize = 0) 

class Loop(): 
    aborted = False 

    def __init__(self): 
     checker = threading.Thread(target=self.checker) 
     checker.setDaemon(True) 
     checker.start() 

     loop = threading.Thread(target=self.loop) 
     loop.start() 


    def checker(self): 
     while True: 
      if q.get() == 2: 
       q.task_done() 
       if not self.aborted: 
        self.aborted = True 
       else: 
        self.aborted = False 


    def loop(self): 
     cnt = 0 

     while cnt < 40: 
      if self.aborted: 
       while self.aborted: 
        print "waiting" 
        time.sleep(.1) 
      print cnt 
      cnt += 1 
      time.sleep(.1) 


class fakeListener(): 

    def __init__(self): 
     listener = threading.Thread(target=self.listener) 
     listener.setDaemon(True) 
     listener.start() 

    def listener(self): 
     while True: 
      q.put(2) 
      time.sleep(1) 



if __name__ == '__main__': 

    #fake listener standing in for the real OSC event listener 
    listener = fakeListener() 

    loop = Loop() 
+2

Voici votre exemple réécrit pour utiliser le multiprocessing http://codepad.org/IRz04lkH – jfs

+0

Quel est l'avantage de la librairie multiproc over threading? Je cours généralement sur un mac avec un i5 donc avoir plusieurs cœurs pour travailler avec. mais si cela se passe sur une machine sans plusieurs cœurs, est-ce simplement une implémentation par défaut? – mix

+0

Pour l'exemple trivial, peu importe, cela dépend de ce que fait votre vrai programme. Python a GIL qui sérialise l'accès à la plupart des parties de l'interpréteur. Mindblowing Python GIL http://blip.tv/file/2232410 – jfs

Répondre

4

Umm .. Je ne comprends pas complètement votre question mais je ferai de mon mieux pour expliquer ce que je pense que vous devez corriger vos problèmes.

1) Le thread de votre fonction Loop.loop doit être défini en tant que thread de démon pour qu'il sorte avec votre thread principal (ainsi vous ne devez pas tuer le processus python chaque fois que vous voulez arrêter votre programme). Pour ce faire, mettez loop.setDaemon (True) avant d'appeler la fonction "start" du thread.

2) La manière la plus simple et la plus infaillible de communiquer entre threads est avec une file d'attente. Le thread mettra un élément dans cette file d'attente et un autre thread sortira un élément, fera quelque chose avec l'élément et se terminera (ou obtiendra un autre travail)

En python, une file d'attente peut être quelque chose d'une liste globale à un python construit -in Objet file d'attente Je recommande la file d'attente python car elle est sûre et facile à utiliser.

+0

si un thread met un élément dans la file d'attente comment un autre thread sait le saisir? Est-ce que le deuxième thread doit juste vérifier périodiquement à la recherche de nouveaux éléments?Merci pour l'astuce du démon. – mix

+0

+1 [Queue] (http://docs.python.org/library/queue.html) est génial pour passer des messages en Python (si vous aimez faire ce genre de choses vous-même). Il prend en charge les opérations de blocage et de non-blocage. –

+1

@mix: vous venez d'appeler 'queue.get()' dans votre thread et il se bloque indéfiniment par défaut (jusqu'à ce qu'un élément soit disponible). – jfs