如何等到只有第一个线程在Python中完成

时间:2013-07-10 07:30:11

标签: python multithreading

要求是启动五个线程,并且只在最快的线程中等待。所有五个线程都在寻找相同的数据5个方向,一个足以继续控制流程。

实际上,我需要等待前两个线程返回,以相互验证。但我想如果我知道如何等待最快的话。我可以弄清楚如何等待第二快的。

很多关于join(timeout)的讨论,但你事先并不知道哪一个要等待(哪一个提前申请join)。

5 个答案:

答案 0 :(得分:15)

使用队列:完成后每个线程将结果放在队列上,然后你只需要读取适当数量的结果并忽略其余部分:

#!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()

Queue.get()的文档(没有任何参数,它等同于Queue.get(True, None)

  

Queue.get([block [,timeout]])

     

从中删除并返回一个项目   队列。如果可选的args块为true且timeout为None(   默认情况下,必要时阻止项目可用。如果超时   一个正数,它最多会阻塞超时秒并提升   如果在该时间内没有可用项目,则清空例外。除此以外   (块为假),如果一个项目立即可用,则返回一个项目,否则   提高Empty异常(在这种情况下忽略超时)。

答案 1 :(得分:4)

如果您的线程中有某种处理循环,则当使用threading.Event()终止时,以下代码将终止它们:

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"

如果您的流程“便宜”或单个请求 - 响应类型线程(例如异步HTTP请求),则Duncan's answer是一种很好的方法。

答案 2 :(得分:1)

您可以使用此事件。见http://docs.python.org/2/library/threading.html#event-objects 这个想法是工作线程在完成时引发事件。主线程在继续之前等待此事件。工作线程可以设置(互斥)变量以使用事件标识自己。

答案 3 :(得分:1)

或者只是跟踪列表中所有已完成的线程,让第二个线程完成处理应该完成的任何事情,Python列表是线程安全的。

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()

答案 4 :(得分:1)

Duncan的方法可能是最好的,也是我推荐的方法。我之前因为缺乏“等待下一个完成的线程完成”而感到有点恼火,所以我只是写了这个来尝试一下。似乎工作。只需使用MWThread代替threading.thread即可获得此wait_for_thread新功能。

全局变量有点笨拙;另一种方法是使它们成为类级变量。但如果它隐藏在一个模块(mwthread.py或其他)中,它应该没问题。

#! /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 __name__ == '__main__':
    main()