2016-03-26 5 views
1

Ich habe ein Python-GUI-Programm, das die gleiche Aufgabe, aber mit mehreren Threads ausführen muss. Das Problem ist, dass ich die Threads anrufe, aber sie nicht parallel, sondern sequentiell ausführen. Die erste wird ausgeführt, sie endet und dann die zweite usw. Ich möchte, dass sie unabhängig voneinander beginnen.Python-GUI bleibt eingefroren, bis der Thread-Code fertig ausgeführt wird

Die Hauptkomponenten sind:
1. Menü (Ansicht)
2. ProcesStarter (Controller)
3. Prozess (Controller)

Das Menü ist, wo Sie auf "Start" klicken Taste, die eine Funktion bei ProcesStarter aufruft.

Die ProcesStarter erstellt Objekte von Prozess und Fäden und startet alle Threads in einer for-Schleife.

Menü:

class VotingFrame(BaseFrame): 

    def create_widgets(self): 
    self.start_process = tk.Button(root, text="Start Process", command=lambda: self.start_process()) 
    self.start_process.grid(row=3,column=0, sticky=tk.W) 

    def start_process(self): 
    procesor = XProcesStarter() 
    procesor_thread = Thread(target=procesor.start_process()) 
    procesor_thread.start() 

ProcesStarter:

class XProcesStarter: 

    def start_process(self): 
     print "starting new process..." 

     # thread count 
     thread_count = self.get_thread_count() 

     # initialize Process objects with data, and start threads 
     for i in range(thread_count): 
      vote_process = XProcess(self.get_proxy_list(), self.get_url()) 
      t = Thread(target=vote_process.start_process()) 
      t.start() 

Prozess:

class XProcess(): 

    def __init__(self, proxy_list, url, browser_show=False): 
     # init code 

    def start_process(self): 
     # code for process 

Wenn ich drücken Sie die GUI-Taste f oder "Start Process" die GUI ist gesperrt, bis beide Threads die Ausführung beenden. Die Idee ist, dass Threads im Hintergrund arbeiten und parallel arbeiten sollten.

Antwort

2

Eine Möglichkeit, eine Klasse als target eines Threads zu verwenden, besteht darin, die Klasse als Ziel und die Argumente für den Konstruktor als args zu verwenden.

from threading import Thread 
from time import sleep 
from random import randint 

class XProcesStarter: 
    def __init__(self, thread_count): 
    print ("starting new process...") 
    self._i = 0 
    for i in range(thread_count): 
     t = Thread(
     target=XProcess, 
     args=(self.get_proxy_list(), self.get_url()) 
    ) 
     t.start() 


    def get_proxy_list(self): 
    self._i += 1 
    return "Proxy list #%s" % self._i 

    def get_url(self): 
    self._i += 1 
    return "URL #%d" % self._i 

class XProcess():  
    def __init__(self, proxy_list, url, browser_show=False): 
    r = 0.001 * randint(1, 5000) 
    sleep(r) 
    print (proxy_list) 
    print (url) 

def main(): 
    t = Thread(target=XProcesStarter, args=(4,)) 
    t.start() 

if __name__ == '__main__': 
    main() 

Dieser Code wird in Python2 und Python3 ausgeführt.

Der Grund ist, dass das Ziel eines Thread-Objekts aufrufbar sein muss (suchen Sie nach "callable" und "__call__" in der Python-Dokumentation für eine vollständige Erklärung).

Bearbeiten Der andere Weg wurde in den Antworten anderer Leute erklärt (siehe Tadhg McDonald-Jensen).

+2

nicht ganz richtig, 'Thread (target = None) .start()' vollständig gültig ist, nicht zu erwähnen, dass eine Methode für ein Objekt ist aufrufbar, also ist nichts falsch daran, es zuerst zu initialisieren und dann die Methode in einem Thread auszuführen. –

+1

Danke! Ich habe heute etwas gelernt! (Ich werde auch meine Antwort bearbeiten) –

+0

Code geändert und es funktioniert wie erwartet! – nullwriter

3

Sie procesor.start_process() sofort anrufen, wenn es als Ziel des Gewindes festgelegt wird:

#use this 
procesor_thread = Thread(target=procesor.start_process) 

#not this 
procesor_thread = Thread(target=procesor.start_process()) 
          # this is called right away^

Wenn Sie nennen es sofort gibt sie keine, die ein gültiges Ziel für Thema ist (es gerade tut nichts) weshalb es passiert sequentiell, die Threads machen nichts.

+0

Das hätte auch so gut funktioniert, schneller bearbeiten als @Sci Progs Antwort. Aber ich verstehe jetzt wieso :) – nullwriter

2

Ich glaube, dein Problem ist, dass an beiden Stellen Threads gestartet werden, du rufst tatsächlich die Methode an, die du als target an den Thread übergeben willst. Das führt seinen Code in dem Hauptthread aus (und versucht, den neuen Thread auf dem Rückgabewert zu starten, falls vorhanden, sobald es fertig ist).

Versuchen:

procesor_thread = Thread(target=procesor.start_process) # no() after start_process 

Und:

t = Thread(target=vote_process.start_process) # no() here either 
+0

Danke, hol es dir jetzt. Ich habe mich gefragt, warum es gerade heißt, indem ich einfach "target = procesor.start_process()" sage – nullwriter

Verwandte Themen