如何同时启动两个功能,只等待更快的一个?
Posted
技术标签:
【中文标题】如何同时启动两个功能,只等待更快的一个?【英文标题】:How to start two functions at the same time and only wait for the faster one? 【发布时间】:2021-01-31 04:12:11 【问题描述】:我有一个工作代码,但不确定这是正确的方法。我有两个函数,都发出一个可能需要 1 到 5 秒之间任意时间的 API 请求,但两者都旨在返回相同的输出。我想同时运行这两个,一旦更快的完成它的工作,终止另一个并丢弃它会返回的任何东西。
p1 = Process(target = search1, args=(name) )
p2 = Process(target = search2, args=(name) )
if __name__=='__main__':
p1.start()
p2.start()
while p1.is_alive() and p2.is_alive():
time.sleep(0.2)
if not p1.is_alive():
p2.terminate()
if not p2.is_alive():
p1.terminate()
如果我不等待一段时间(在这种情况下为 0.2 秒),有时如果两者花费的时间相同,则两者都会返回。我测试了很多次并且它有效,但这是正确的方法吗?这种方法是否会出现任何问题?
--- ti7 建议
在 ti7 的建议下尝试使用线程,现在它可以使用线程而不是进程。
def search1(Q_result, name):
result = somefunction()
Q_result.put(result)
def search2(Q_result, name):
time.sleep(10)
result = somefunction()
Q_result.put(result )
import Queue as queue
import threading
Q_result = queue.Queue() # create a Queue to hold the result(s)
if __name__=='__main__':
t1 = threading.Thread(
target=search1,
args=(Q_result, name),
)
t1.daemon = True
t1.start()
t2 = threading.Thread(
target=search2,
args=(Q_result),
)
t2.daemon = True
t2.start()
print(Q_result.get())
【问题讨论】:
你有很多这样的任务要做吗?因为对于数千个任务来说,启动和终止进程需要相当多的时间和资源。此外,如果它只是一个 API 请求,那么您可能可以使用与threading
模块中的轻量级线程相同的方法。但总的来说,您的方法还不错,是解决任务的一种方法。但是从某种意义上说,您的解决方案很好,因为它可以清除所有资源,因为当进程被杀死时,所有资源都会被释放。但是如果你使用线程,那么一些资源可能会被泄露。
我还有一个想法 - 如果您必须执行许多任务、许多请求,并且您的下一个请求不依赖于先前请求的结果,那么您可以同时执行 2-3 个相同的 API 请求可以同时做不同的请求,很多是并行的。只有当其中一些请求失败时,才重试第二次。这种技术不仅可以让您提高总体性能,还可以减少 API 服务器上不必要的重量。
这是一个移动客户端的搜索功能,所以是的,这些功能甚至可以在当前高峰使用时同时运行 20-30 次。你推荐在这种情况下使用 threading 模块吗?
顺便说一句,有一些自动化工具可以自动转换 Py2->Py3。一个是Modernizer,另一个是Futurize。第二个是一件很酷的事情,因为它以这样一种方式进行转换,之后 Py2 和 Py3 都可以运行相同的脚本。此外,Futurizer 只是在脚本的开头添加了额外的导入,这些导入使您的进一步代码可以同时由 Py2 和 Py3 运行,因此您的脚本几乎没有修改但升级了。我心目中的理想解决方案。
顺便说一句,@ti7 刚刚更新了他对另一个守护进程解决方案的回答,而不是 daemon = True
构造函数参数,您可以在 Python 2 中的线程 t
实例上使用 t.daemon = True
。
【参考方案1】:
如果您多次提出相同的请求,您最好只做一次并联系服务的所有者以提高其性能。 (例如,它可能正在分发连接并且其中一个节点非常慢)。
正如 @Arty 所说,使用threads 将比创建进程更轻松,因此性能更高。您可以使线程成为守护进程,因此它们不需要.join()
ed 即可退出(阻止程序退出,直到所有线程都完成)。
异步逻辑可能会更快一些,但推理起来可能会令人沮丧,尤其是在 Python 2 中。此外,您可能会发现,如果您使用的是 3rd-party 库,例如 Twisted's Defferred,会加载所需的库速度非常慢并会降低整体性能。
使用线程,您可能会发现获取结果并将其放入queue.Queue
很方便,它既是线程安全的,又可以block until content is available。
粗线示例
from __future__ import print_function # print(x) over print x
import queue
import threading
# if these are the same logic, use an argument to differentiate
# otherwise you could have any number of unique functions,
# each of which makes some different request
def make_request(Q_result, request_args):
result = "whatever logic is needed to make request A"
Q_result.put(result) # put the result into the Queue
list_of_different_request_args = [] # fill with whatever you need
Q_result = queue.Queue() # create a Queue to hold the result(s)
# iterate over input args (could be list of target functions instead)
for request_args in list_of_different_request_args:
t = threading.Thread(
target=make_request,
args=(Q_result, request_args),
)
t.daemon = True # set via arg in Python 3
t.start()
# get the first result, blocking until one is available
print(Q_result.get())
# program exits and discards threads
【讨论】:
嗨!我根据您的建议编辑了我的代码,现在看看这个问题。问题是它不接受守护进程作为 init 中的关键字。如果我删除它,它会运行,但较慢的线程会阻止退出。可能是什么问题? @AkosF 对不起!自从我使用 Python 2 以来已经有一分钟了;这是一个应该在启动线程之前设置的属性 - 我已经更新了我的示例!文档:docs.python.org/2/library/… 非常感谢您的帮助,现在它就像魅力一样! 嗨@ti7!希望你看到这个问题 :) 如果两个线程都因异常而失败,因此主线程被永远阻塞,我正在努力处理这种情况。知道在这种情况下如何安全地退出两个线程吗?提前非常感谢! @AkosF 请提出一个新问题并将其链接到此处,再次标记我,但Queue.get()
接受超时参数,这将允许您停止阻塞并继续退出(或您的任何默认值)喜欢)文档:docs.python.org/3/library/queue.html#queue.Queue.get以上是关于如何同时启动两个功能,只等待更快的一个?的主要内容,如果未能解决你的问题,请参考以下文章