De niveau débutant en Python les problèmes de threading
Comme quelqu'un de nouveau développement du GUI en Python (avec pyGTK), j'ai juste commencé à apprendre à propos de filetage. Pour tester mes compétences, j'ai écrit une simple petite interface GTK avec un bouton start/stop. L'objectif est que, lorsqu'il est cliqué, un thread commence rapidement par incréments d'un nombre dans la zone de texte, tout en gardant l'interface graphique responsive.
J'ai l'interface graphique fonctionne très bien, mais éprouve des difficultés avec le filetage. C'est probablement un problème simple, mais mon esprit est sur frites pour la journée. Ci-dessous, j'ai collé la première trackback à partir de l'interpréteur Python, suivi par le code. Vous pouvez aller à http://drop.io/pxgr5id de le télécharger. Je suis en utilisant bzr pour le contrôle de la révision, donc, si vous voulez faire une modification et de re-déposer, s'il vous plaît valider les modifications. Je suis aussi de coller le code à http://dpaste.com/113388/, car cela peut avoir des numéros de ligne, et ce démarque des choses est de me donner un mal de tête.
Mise à jour le 27 janvier, 15:52 HNE:
Légèrement mise à jour de code peut être trouvé ici: http://drop.io/threagui/asset/thread-gui-rev3-tar-gz
Traceback
crashsystems@crashsystems-laptop:~/Desktop/thread-gui$ python threadgui.pybtnStartStop clicked
Traceback (most recent call last):
File "threadgui.py", line 39, in on_btnStartStop_clicked
self.thread.stop()
File "threadgui.py", line 20, in stop
self.join()
File "/usr/lib/python2.5/threading.py", line 583, in join
raise RuntimeError("cannot join thread before it is started")
RuntimeError: cannot join thread before it is started
btnStartStop clicked
threadStop = 1
btnStartStop clicked
threadStop = 0
btnStartStop clicked
Traceback (most recent call last):
File "threadgui.py", line 36, in on_btnStartStop_clicked
self.thread.start()
File "/usr/lib/python2.5/threading.py", line 434, in start
raise RuntimeError("thread already started")
RuntimeError: thread already started
btnExit clicked
exit() called
Code
#!/usr/bin/bash
import gtk, threading
class ThreadLooper (threading.Thread):
def __init__ (self, sleep_interval, function, args=[], kwargs={}):
threading.Thread.__init__(self)
self.sleep_interval = sleep_interval
self.function = function
self.args = args
self.kwargs = kwargs
self.finished = threading.Event()
def stop (self):
self.finished.set()
self.join()
def run (self):
while not self.finished.isSet():
self.finished.wait(self.sleep_interval)
self.function(*self.args, **self.kwargs)
class ThreadGUI:
# Define signals
def on_btnStartStop_clicked(self, widget, data=None):
print "btnStartStop clicked"
if(self.threadStop == 0):
self.threadStop = 1
self.thread.start()
else:
self.threadStop = 0
self.thread.stop()
print "threadStop = " + str(self.threadStop)
def on_btnMessageBox_clicked(self, widget, data=None):
print "btnMessageBox clicked"
self.lblMessage.set_text("This is a message!")
self.msgBox.show()
def on_btnExit_clicked(self, widget, data=None):
print "btnExit clicked"
self.exit()
def on_btnOk_clicked(self, widget, data=None):
print "btnOk clicked"
self.msgBox.hide()
def on_mainWindow_destroy(self, widget, data=None):
print "mainWindow destroyed!"
self.exit()
def exit(self):
print "exit() called"
self.threadStop = 1
gtk.main_quit()
def threadLoop(self):
# This will run in a thread
self.txtThreadView.set_text(str(self.threadCount))
print "hello world"
self.threadCount += 1
def __init__(self):
# Connect to the xml GUI file
builder = gtk.Builder()
builder.add_from_file("threadgui.xml")
# Connect to GUI widgets
self.mainWindow = builder.get_object("mainWindow")
self.txtThreadView = builder.get_object("txtThreadView")
self.btnStartStop = builder.get_object("btnStartStop")
self.msgBox = builder.get_object("msgBox")
self.btnMessageBox = builder.get_object("btnMessageBox")
self.btnExit = builder.get_object("btnExit")
self.lblMessage = builder.get_object("lblMessage")
self.btnOk = builder.get_object("btnOk")
# Connect the signals
builder.connect_signals(self)
# This global will be used for signaling the thread to stop.
self.threadStop = 1
# The thread
self.thread = ThreadLooper(0.1, self.threadLoop, (1,0,-1))
self.threadCounter = 0
if __name__ == "__main__":
# Start GUI instance
GUI = ThreadGUI()
GUI.mainWindow.show()
gtk.main()
OriginalL'auteur |
Vous devez vous connecter pour publier un commentaire.
Filetage avec PyGTK est un peu difficile si vous voulez bien faire les choses. Fondamentalement, vous ne devez pas mettre à jour GUI partir de n'importe quel autre thread que le thread principal (limitation commune en GUI libs). Habituellement, cela se fait en PyGTK à l'aide du mécanisme de file d'attente de messages (pour la communication entre les travailleurs et GUI) qui sont lus régulièrement à l'aide de fonction de délai. Une fois j'ai eu une présentation sur mon gul local à ce sujet, vous pouvez saisir un code d'exemple pour cette présentation de Google référentiel de Code. Jetez un oeil à
MainWindow
classe dansforms/frmmain.py
, spécialement pour la méthode_pulse()
et ce qui est fait danson_entry_activate()
(thread est démarré il ya en plus le minuteur d'inactivité est créé).De cette façon, les mises à jour des applications GUI quand est "inactive" (par GTK moyens) causant aucun blocage.
OriginalL'auteur
Généralement, il est préférable d'éviter les fils quand vous le pouvez. Il est très difficile d'écrire une tige de demande correctement, et encore plus difficile de savoir vous l'avez droit. Puisque vous êtes en train de rédiger une application graphique, il est plus facile pour vous de visualiser comment le faire, puisque vous avez déjà commencé à écrire votre demande dans un délai asynchrone cadre.
La chose importante à réaliser est que l'interface graphique de l'application est en train de faire tout un tas de rien. Il passe la plupart de son temps à attendre pour le système d'exploitation pour dire que quelque chose s'est passé. Vous pouvez faire beaucoup de choses dans ce temps d'inactivité aussi longtemps que vous savez comment écrire de longs-exécution de code de sorte qu'il ne bloque pas.
Vous pouvez résoudre votre problème d'origine à l'aide d'un délai d'attente; en parler à votre GUI cadre pour appeler une fonction après un temps de retard, et alors que la réinitialisation de retard ou de départ d'un autre appel tardif.
Une autre question est de savoir comment communiquer sur le réseau dans une application graphique. Réseau applis sont comme des applications à interface graphique dans ce qu'ils font tout un tas d'attente. À l'aide d'un réseau IO cadre (comme Twisted) il est facile d'avoir les deux parties de votre application d'attente en collaboration plutôt qu'en concurrence, et de nouveau réduit le besoin pour d'autres threads.
Long exécution de calculs peuvent être écrites de manière itérative au lieu de façon synchrone, et vous pouvez faire de votre traitement, tandis que l'interface utilisateur est inactif. Vous pouvez utiliser un générateur de le faire assez facilement en python.
Appel
long_calculation
vous donnera un objet de générateur, et en appelant.next()
sur l'objet du générateur va exécuter le générateur jusqu'à ce qu'il atteint soityield
oureturn
. Vous venez de dire à l'interface graphique cadre de l'appellong_calculation(some_param, some_callback).next
quand il a le temps, et éventuellement de votre callback sera appelée avec le résultat.Je ne sais pas GTK très bien, donc je ne peux pas vous dire qui gobject fonctions vous devez appeler. Avec cette explication, cependant, vous devriez être capable de trouver les fonctions nécessaires dans la documentation, ou, au pire, demander sur un canal IRC.
Malheureusement, il n'existe pas de bonne-affaire de réponse. Si vous préciser exactement ce que vous êtes en train de faire, il serait plus facile d'expliquer pourquoi vous n'avez pas besoin de threads dans cette situation.
Si vous pouvez obtenir un descripteur de fichier pour l'interface série, vous pouvez probablement qu'en mode non bloquant et de les rendre non bloquant les lectures et les écritures sur l'interface. Je ne recommandent pas l'utilisation de cette bibliothèque. J'ai regardé rapidement, et il n'est pas très bien écrit.
Eh bien c'est génial d'entendre! Malheureusement, le rotor n'a qu'une interface série. Sait de toute bonne série de modules de Python?
pyserial.wiki.sourceforge.net/pySerial À mon avis python avec pySerial est votre meilleur choix parmi tous les langages/technologies si vous voulez faire de la croix-plate-forme des applications de port série.
OriginalL'auteur
Vous ne pouvez pas redémarrer un arrêté objet thread de ne pas essayer. Au lieu de cela, créer une nouvelle instance de l'objet si vous voulez le redémarrer après c'est vraiment arrêté et a rejoint.
OriginalL'auteur
J'ai joué avec différents outils pour aider à nettoyer le travail avec les threads, en attente de traitement, etc.
make_idle est une fonction décorateur qui vous permet d'exécuter une tâche en arrière-plan dans la coopération. C'est un bon compromis entre quelque chose d'assez court pour exécuter une seule fois dans le thread d'INTERFACE utilisateur et de ne pas affecter la réactivité de l'application et de faire un thread spécial de la synchronisation. L'intérieur décoré de la fonction que vous utilisez le "rendement" à la main le traitement de l'interface graphique, donc il peut rester réactive et la prochaine fois que l'INTERFACE utilisateur est inactif il sera ramasser dans votre fonction d'où vous l'avez laissé. Donc, pour obtenir cela a commencé, vous appelez simplement idle_add à l'décoré de la fonction.
Si vous avez besoin de faire un peu plus de traitement, vous pouvez utiliser un gestionnaire de contexte pour verrouiller le thread d'INTERFACE utilisateur en cas de besoin pour aider à rendre le code un peu plus sûr de
avec qui vous pouvez simplement
Je n'ai pas fini encore, mais en combinant faire les choses purement au ralenti et purement dans un thread, j'ai un décorateur (pas encore testé donc pas affiché sur le site) que vous pouvez dire si la prochaine section après le rendement est à exécuter dans l'INTERFACE utilisateur est temps d'inactivité ou dans un thread. Cela permettrait à certains d'installation dans le thread de l'INTERFACE utilisateur, de passer à un nouveau thread pour faire des trucs, et puis passer de l'un à l'INTERFACE utilisateur est en veille, le temps de faire le nettoyage, réduisant ainsi le besoin pour les serrures.
OriginalL'auteur
Je n'ai pas regardé en détail sur votre code. Mais je vois deux solutions à votre problème:
Ne pas utiliser des threads. Au lieu d'utiliser un délai d'attente, comme ceci:
Lors de l'utilisation des threads, vous devez vous assurer que votre code de la GUI est seulement appelé à partir d'un thread en même temps, en gardant comme ceci:
OriginalL'auteur