Python 如果执行在超时内完成,则从函数返回,否则进行回调
我有一个Python 3.5项目,没有使用任何异步特性。我必须实现以下逻辑:Python 如果执行在超时内完成,则从函数返回,否则进行回调,python,python-3.x,asynchronous,async-await,python-asyncio,Python,Python 3.x,Asynchronous,Async Await,Python Asyncio,我有一个Python 3.5项目,没有使用任何异步特性。我必须实现以下逻辑: def should_return_in_3_sec(some_serious_job, arguments, finished_callback): # Start some_serious_job(*arguments) in a task # if it finishes within 3 sec: # return result immediately # otherwis
def should_return_in_3_sec(some_serious_job, arguments, finished_callback):
# Start some_serious_job(*arguments) in a task
# if it finishes within 3 sec:
# return result immediately
# otherwise return None, but do not terminate task.
# If the task finishes in 1 minute:
# call finished_callback(result)
# else:
# call finished_callback(None)
pass
函数应该在3秒内返回,应该保持同步,但是我可以编写任何新的异步代码,包括一些严肃的任务
做这件事最优雅和最具Python风格的方法是什么?分叉一个执行严肃任务的线程,让它将结果写入队列,然后终止。以三秒的超时时间从该队列读入主线程。如果发生超时,则启动另一个线程并返回None。让第二个线程以一分钟的超时时间从队列中读取数据;如果也超时,则调用finished\u callbackNone;否则调用finished_callbackresult 我是这样画的:
import threading, queue
def should_return_in_3_sec(some_serious_job, arguments, finished_callback):
result_queue = queue.Queue(1)
def do_serious_job_and_deliver_result():
result = some_serious_job(arguments)
result_queue.put(result)
threading.Thread(target=do_serious_job_and_deliver_result).start()
try:
result = result_queue.get(timeout=3)
except queue.Empty: # timeout?
def expect_and_handle_late_result():
try:
result = result_queue.get(timeout=60)
except queue.Empty:
finished_callback(None)
else:
finished_callback(result)
threading.Thread(target=expect_and_handle_late_result).start()
return None
else:
return result
分叉一个执行严肃任务的线程,让它将其结果写入队列,然后终止。以三秒的超时时间从该队列读入主线程。如果发生超时,则启动另一个线程并返回None。让第二个线程以一分钟的超时时间从队列中读取数据;如果也超时,则调用finished\u callbackNone;否则调用finished_callbackresult 我是这样画的:
import threading, queue
def should_return_in_3_sec(some_serious_job, arguments, finished_callback):
result_queue = queue.Queue(1)
def do_serious_job_and_deliver_result():
result = some_serious_job(arguments)
result_queue.put(result)
threading.Thread(target=do_serious_job_and_deliver_result).start()
try:
result = result_queue.get(timeout=3)
except queue.Empty: # timeout?
def expect_and_handle_late_result():
try:
result = result_queue.get(timeout=60)
except queue.Empty:
finished_callback(None)
else:
finished_callback(result)
threading.Thread(target=expect_and_handle_late_result).start()
return None
else:
return result
线程模块有一些简单的超时选项,例如,请参阅Thread.jointimeout
如果您确实选择使用asyncio,下面是一个部分解决方案,可以满足您的一些需求:
import asyncio
import time
async def late_response(task, flag, timeout, callback):
done, pending = await asyncio.wait([task], timeout=timeout)
callback(done.pop().result() if done else None) # will raise an exception if some_serious_job failed
flag[0] = True # signal some_serious_job to stop
return await task
async def launch_job(loop, some_serious_job, arguments, finished_callback,
timeout_1=3, timeout_2=5):
flag = [False]
task = loop.run_in_executor(None, some_serious_job, flag, *arguments)
done, pending = await asyncio.wait([task], timeout=timeout_1)
if done:
return done.pop().result() # will raise an exception if some_serious_job failed
asyncio.ensure_future(
late_response(task, flag, timeout_2, finished_callback))
return None
def f(flag, n):
for i in range(n):
print("serious", i, flag)
if flag[0]:
return "CANCELLED"
time.sleep(1)
return "OK"
def finished(result):
print("FINISHED", result)
loop = asyncio.get_event_loop()
result = loop.run_until_complete(launch_job(loop, f, [1], finished))
print("result:", result)
loop.run_forever()
这将在单独的线程中运行作业。请使用loop.set_executorProcessPoolExecutor在进程中运行CPU密集型任务。请记住,终止进程/线程是一种不好的做法-上面的代码使用一个非常简单的列表来通知线程停止。另请参见threading.Event/multiprocessing.Event
在实现解决方案时,您可能会发现您希望修改现有代码以使用例程而不是线程。线程模块有一些简单的超时选项,例如,请参阅Thread.jointimeout
如果您确实选择使用asyncio,下面是一个部分解决方案,可以满足您的一些需求:
import asyncio
import time
async def late_response(task, flag, timeout, callback):
done, pending = await asyncio.wait([task], timeout=timeout)
callback(done.pop().result() if done else None) # will raise an exception if some_serious_job failed
flag[0] = True # signal some_serious_job to stop
return await task
async def launch_job(loop, some_serious_job, arguments, finished_callback,
timeout_1=3, timeout_2=5):
flag = [False]
task = loop.run_in_executor(None, some_serious_job, flag, *arguments)
done, pending = await asyncio.wait([task], timeout=timeout_1)
if done:
return done.pop().result() # will raise an exception if some_serious_job failed
asyncio.ensure_future(
late_response(task, flag, timeout_2, finished_callback))
return None
def f(flag, n):
for i in range(n):
print("serious", i, flag)
if flag[0]:
return "CANCELLED"
time.sleep(1)
return "OK"
def finished(result):
print("FINISHED", result)
loop = asyncio.get_event_loop()
result = loop.run_until_complete(launch_job(loop, f, [1], finished))
print("result:", result)
loop.run_forever()
这将在单独的线程中运行作业。请使用loop.set_executorProcessPoolExecutor在进程中运行CPU密集型任务。请记住,终止进程/线程是一种不好的做法-上面的代码使用一个非常简单的列表来通知线程停止。另请参见threading.Event/multiprocessing.Event
在实现您的解决方案时,您可能会发现您希望修改现有代码以使用例程而不是线程。Nice!虽然我并没有发现严重的工作终止后超时。。。我还想知道,在这个多线程示例中使用标准队列可以吗?队列模块用于线程上下文,是的。它显式地同步它的使用。当然,如果终止分离线程时会出现问题,则会有一个小问题,这取决于您的上下文。但是你给他们的规格不允许他们再次加入。太好了!虽然我并没有发现严重的工作终止后超时。。。我还想知道,在这个多线程示例中使用标准队列可以吗?队列模块用于线程上下文,是的。它显式地同步它的使用。当然,如果终止分离线程时会出现问题,则会有一个小问题,这取决于您的上下文。但是你给他们的规格不允许他们再次正确连接。