如何等到 Python 中只有第一个线程完成
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17564804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wait until only the first thread is finished in Python
提问by u1758793
The requirement is to start five threads, and wait only in the fastest thread. All five threads went to look for the same data 5 directions, and one is enough to continue the control flow.
要求是启动五个线程,只在最快的线程中等待。所有五个线程都去5个方向寻找相同的数据,一个就足以继续控制流。
Actually, I need to wait for the first two threads to return, to verify against each other. But I guess if I know how to wait for the fastest. I can figure out how to wait for the second-fastest.
实际上,我需要等待前两个线程返回,以相互验证。但我想如果我知道如何等待最快。我可以弄清楚如何等待第二快。
A lot talk about join(timeout)
, but you don't know in advance which one to wait (which one to apply join
in advance).
谈了很多join(timeout)
,但你事先不知道该等哪一个(join
提前申请哪一个)。
回答by Ludo
You can use an event for this. See http://docs.python.org/2/library/threading.html#event-objectsThe idea is that the worker threads raise an event when they are finished. The main thread waits for this event before continueing. The worker thread can set a (mutexed) variable to identify itself with the event.
您可以为此使用事件。请参阅http://docs.python.org/2/library/threading.html#event-objects这个想法是工作线程在完成时引发一个事件。主线程在继续之前等待此事件。工作线程可以设置一个(互斥的)变量来用事件标识自己。
回答by Fredrik H??rd
Or just keep track of all finished threads in a list and let the second thread to finish handle whatever is supposed to be done, Python lists are threadsafe.
或者只是跟踪列表中所有完成的线程,让第二个完成的线程处理应该完成的任何事情,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()
回答by will-hart
If you have some sort of processing loop in your threads, the following code will terminate them when one terminates by using a threading.Event():
如果您的线程中有某种处理循环,以下代码将在使用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"
If your process is "cheap" or a single request-response type thread (i.e. for instance an async HTTP request) then Duncan's answeris a good approach.
如果您的流程“便宜”或单个请求-响应类型线程(例如异步 HTTP 请求),那么Duncan 的回答是一个很好的方法。
回答by Duncan
Use a queue: each thread when completed puts the result on the queue and then you just need to read the appropriate number of results and ignore the remainder:
使用队列:每个线程在完成时将结果放在队列中,然后您只需要读取适当数量的结果并忽略余数:
#!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()
Documentation for Queue.get()
(with no arguments it is equivalent to Queue.get(True, None)
:
的文档Queue.get()
(没有参数,它相当于Queue.get(True, None)
:
Queue.get([block[, timeout]])
Remove and return an item from the queue. If optional args block is true and timeout is None (the default), block if necessary until an item is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Empty exception if no item was available within that time. Otherwise (block is false), return an item if one is immediately available, else raise the Empty exception (timeout is ignored in that case).
Queue.get([block[, timeout]])
从队列中移除并返回一个项目。如果可选 args 块为 true 且 timeout 为 None (默认值),则在必要时阻止,直到项目可用。如果 timeout 是一个正数,它最多阻塞 timeout 秒,如果在该时间内没有可用的项目,则引发 Empty 异常。否则(块为假),如果一个项目立即可用,则返回一个项目,否则引发 Empty 异常(在这种情况下忽略超时)。
回答by torek
Duncan's method is probably the best and is what I would recommend. I've been mildly annoyed by the lack of "wait for next completed thread to complete" before, though, so I just wrote this up to try it out. Seems to work. Simply use MWThread
in place of threading.thread
and you get this new wait_for_thread
function.
邓肯的方法可能是最好的,也是我推荐的方法。不过,之前没有“等待下一个完成的线程完成”让我有点恼火,所以我只是写了这个来尝试一下。似乎工作。只需使用MWThread
代替即可threading.thread
获得此新wait_for_thread
功能。
The global variables are a bit klunky; an alternative would be to make them class-level variables. But if this is hidden in a module (mwthread.py or whatever) it should be fine either way.
全局变量有点笨拙;另一种方法是使它们成为类级变量。但是如果它隐藏在一个模块(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()