it-swarm.com.de

Wie warten Sie, bis in Python nur der erste Thread fertig ist

Die Anforderung ist, fünf Threads zu starten und nur im schnellsten Thread zu warten. Alle fünf Threads suchten nach den gleichen Datenrichtungen, und einer reicht aus, um den Kontrollfluss fortzusetzen.

Eigentlich muss ich warten, bis die ersten beiden Threads zurückkehren, um sich gegenseitig zu überprüfen. Aber ich denke, wenn ich weiß, wie man auf das schnellste wartet. Ich kann herausfinden, wie man auf den Zweitschnellsten wartet.

Es wird viel über join(timeout) geredet, aber Sie wissen nicht im Voraus, welche Sie warten (welche join vorher angewendet werden soll).

15
Peer Gynt

Verwenden Sie eine Warteschlange: Nach Abschluss jedes Threads wird das Ergebnis in die Warteschlange gestellt. Dann müssen Sie nur die entsprechende Anzahl von Ergebnissen lesen und den Rest ignorieren:

#!python3.3
import queue    # For Python 2.x use 'import Queue as queue'
import threading, time, random

def func(id, result_queue):
    print("Thread", id)
    time.sleep(random.random() * 5)
    result_queue.put((id, 'done'))

def main():
    q = queue.Queue()
    threads = [ threading.Thread(target=func, args=(i, q)) for i in range(5) ]
    for th in threads:
        th.daemon = True
        th.start()

    result1 = q.get()
    result2 = q.get()

    print("Second result: {}".format(result2))

if __name__=='__main__':
    main()

Dokumentation für Queue.get() (ohne Argumente entspricht es Queue.get(True, None):

Queue.get ([Block [ Timeout]])

Entfernen Sie ein Element aus der Warteschlange , Und geben Sie es zurück. Wenn der optionale args-Block auf true und das Timeout auf None gesetzt ist (Standardeinstellung ), Blockieren Sie gegebenenfalls, bis ein Element verfügbar ist. Wenn Timeout Eine positive Zahl ist, werden maximal Timeout-Sekunden blockiert und die Leere Ausnahme ausgelöst, wenn innerhalb dieser Zeit kein Element verfügbar war. Andernfalls (Block ist falsch), geben Sie ein Element zurück, wenn eines sofort verfügbar ist. Andernfalls wird durch Die leere Ausnahme ausgelöst (das Timeout wird in diesem Fall ignoriert).

15
Duncan

Wenn sich in Ihren Threads eine Art Verarbeitungsschleife befindet, werden sie mit dem folgenden Code beendet, wenn eine mit threading.Event () beendet wird:

def my_thread(stop_event):
    while not stop_event.is_set():
        # do stuff in a loop

        # some check if stuff is complete
        if stuff_complete:
            stop_event.set()
            break

def run_threads():
    # create a thread event
    a_stop_event = threading.Event()

    # spawn the threads
    for x in range(5):
        t = threading.Thread(target=my_thread, args=[a_stop_event])
        t.start()

    while not a_stop_event.is_set():
        # wait for an event
        time.sleep(0.1)

    print "At least one thread is done"

Wenn Ihr Prozess "billig" oder ein einzelner Anforderungsantworttyp ist (z. B. eine asynchrone HTTP-Anforderung), ist Duncans Antwort ein guter Ansatz.

4
will-hart

Oder verfolgen Sie einfach alle erledigten Threads in einer Liste und lassen Sie den zweiten Thread fertigstellen, was auch immer geschehen soll. Python-Listen sind threadsicher.

finished_threads = []
event = threading.Event()

def func():
   do_important_stuff()

   thisthread = threading.current_thread()
   finished_threads.append(thisthread)
   if len(finished_threads) > 1 and finished_threads[1] == thisthread:
       #yay we are number two!
       event.set()

for i in range(5):
    threading.Thread(target=func).start()

event.wait()
1
Fredrik Håård

Duncans Methode ist wahrscheinlich die beste und würde ich empfehlen. Der Mangel an "Warten auf den nächsten abgeschlossenen Thread" war schon vorher etwas ärgerlich, daher habe ich das nur geschrieben, um es auszuprobieren. Scheint zu funktionieren. Verwenden Sie einfach MWThread anstelle von threading.thread und Sie erhalten diese neue wait_for_thread-Funktion.

Die globalen Variablen sind etwas klunky; Eine Alternative wäre, sie auf Klassenebene zu definieren. Wenn dies jedoch in einem Modul verborgen ist (mwthread.py oder was auch immer), sollte es auf jeden Fall in Ordnung sein.

#! /usr/bin/env python

# Example of how to "wait for" / join whichever threads is/are done,
# in (more or less) the order they're done.

import threading
from collections import deque

_monitored_threads = []
_exited_threads = deque()
_lock = threading.Lock()
_cond = threading.Condition(_lock)

class MWThread(threading.Thread):
    """
    multi-wait-able thread, or monitored-wait-able thread
    """
    def run(self):
        tid = threading.current_thread()
        try:
            with _lock:
                _monitored_threads.append(tid)
            super(MWThread, self).run()
        finally:
            with _lock:
                _monitored_threads.remove(tid)
                _exited_threads.append(tid)
                _cond.notifyAll()

def wait_for_thread(timeout=None):
    """
    Wait for some thread(s) to have finished, with optional
    timeout.  Return the first finished thread instance (which
    is removed from the finished-threads queue).

    If there are no unfinished threads this returns None
    without waiting.
    """
    with _cond:
        if not _exited_threads and _monitored_threads:
            _cond.wait(timeout)
        if _exited_threads:
            result = _exited_threads.popleft()
        else:
            result = None
    return result

def main():
    print 'testing this stuff'
    def func(i):
        import time, random
        sleeptime = (random.random() * 2) + 1
        print 'thread', i, 'starting - sleep for', sleeptime
        time.sleep(sleeptime)
        print 'thread', i, 'finished'

    threads = [MWThread(target=func, args=(i,)) for i in range(3)]
    for th in threads:
        th.start()
    i = 0
    while i < 3:
        print 'main: wait up to .5 sec'
        th = wait_for_thread(.5)
        if th:
            print 'main: got', th
            th.join()
            i += 1
        else:
            print 'main: timeout'
    print 'I think I collected them all'
    print 'result of wait_for_thread():'
    print wait_for_thread()

if __== '__main__':
    main()
1
torek

Sie können dafür eine Veranstaltung verwenden. Siehe http://docs.python.org/2/library/threading.html#event-objects Die Idee ist, dass die Arbeitsthreads ein Ereignis auslösen, wenn sie abgeschlossen sind. Der Haupt-Thread wartet auf dieses Ereignis, bevor er fortfährt. Der Arbeitsthread kann eine (mutierte) Variable setzen, um sich mit dem Ereignis zu identifizieren.

1
Ludo