Python异步IO任务的产量很低
我对如何使用Python 3.4中的Python异步IO任务的产量很低,python,asynchronous,python-asyncio,Python,Asynchronous,Python Asyncio,我对如何使用Python 3.4中的asyncio模块感到困惑。我有一个搜索引擎的searchAPI,希望每个搜索请求要么并行运行,要么异步运行,这样我就不必等待一个搜索完成再开始另一个搜索 下面是我的高级搜索API,用于使用原始搜索结果构建一些对象。搜索引擎本身正在使用某种异步机制,所以我不想为此费心 # No asyncio module used here now class search(object): ... self.s = some_search_engine() .
asyncio
模块感到困惑。我有一个搜索引擎的search
API,希望每个搜索请求要么并行运行,要么异步运行,这样我就不必等待一个搜索完成再开始另一个搜索
下面是我的高级搜索API,用于使用原始搜索结果构建一些对象。搜索引擎本身正在使用某种异步机制,所以我不想为此费心
# No asyncio module used here now
class search(object):
...
self.s = some_search_engine()
...
def searching(self, *args, **kwargs):
ret = {}
# do some raw searching according to args and kwargs and build the wrapped results
...
return ret
为了尝试异步请求,我编写了以下测试用例来测试如何将我的东西与asyncio
模块交互
# Here is my testing script
@asyncio.coroutine
def handle(f, *args, **kwargs):
r = yield from f(*args, **kwargs)
return r
s = search()
loop = asyncio.get_event_loop()
loop.run_until_complete(handle(s.searching, arg1, arg2, ...))
loop.close()
通过使用pytest运行,当它到达r=yield from…
行时,它将返回RuntimeError:Task got bad yield:{results from search…}
我还尝试了另一种方法
# same handle as above
def handle(..):
....
s = search()
loop = asyncio.get_event_loop()
tasks = [
asyncio.async(handle(s.searching, arg11, arg12, ...)),
asyncio.async(handle(s.searching, arg21, arg22, ...)),
...
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
通过pytest运行这个测试用例,它通过了,但是搜索引擎会出现一些奇怪的异常。它还说未来/任务异常从未被检索到
我想问的是:
从
生成的正确方法吗搜索
的客户端都需要包含loop=get\u event\u loop()
这种东西来异步请求问题是,您不能像调用
asyncio.coroutine
那样调用现有的同步代码并获取异步行为。当您调用yield from search(…)
时,如果search
本身实际上是一个asyncio.coroutine
,或者至少返回一个asyncio.Future
,那么您只会得到异步行为。现在,search
只是一个常规的同步函数,因此调用yield from search(…)
只会抛出一个错误,因为它不会返回Future
或corroutine
要获得所需的行为,除了同步版本之外,您还需要一个异步版本的搜索
(或者如果不需要同步版本,就完全删除同步版本)。您有几个选项可以同时支持这两种功能:
搜索
重写为一个asyncio.coroutine
,它使用asyncio
兼容的调用来执行其I/O,而不是阻塞I/O。这将使它在asyncio
上下文中工作,但这意味着您将无法在同步上下文中直接调用它。相反,您还需要提供一个替代的同步搜索
方法,该方法启动asyncio
事件循环并调用返回循环。运行直到完成(self.search(…)
)。有关这方面的更多详细信息,请参阅搜索
的同步实现,并提供一个替代的异步API,用于在后台线程中运行搜索
方法:
class search(object):
...
self.s = some_search_engine()
...
def searching(self, *args, **kwargs):
ret = {}
...
return ret
@asyncio.coroutine
def searching_async(self, *args, **kwargs):
loop = kwargs.get('loop', asyncio.get_event_loop())
try:
del kwargs['loop'] # assuming searching doesn't take loop as an arg
except KeyError:
pass
r = yield from loop.run_in_executor(None, self.searching, *args) # Passing None tells asyncio to use the default ThreadPoolExecutor
return r
测试脚本:
s = search()
loop = asyncio.get_event_loop()
loop.run_until_complete(s.searching_async(arg1, arg2, ...))
loop.close()
通过这种方式,您可以保持同步代码的原样,并至少提供可以在asyncio
代码中使用的方法,而不会阻塞事件循环。如果您在代码中实际使用异步I/O,那么它就不是一个干净的解决方案,但总比什么都没有好搜索版本
,一个使用阻塞I/O,另一个兼容asyncio
。这为这两种上下文提供了理想的实现,但需要两倍的工作量杰出的非常感谢你!我看到了一些关于AcsiCIO的C++代码,它们使用了<代码>无效的FO(..){Bin(FuixCalin,Y1,E2,…);},并在<代码> Vo.Poovi-Calin(..)< /Calp>中执行实际的实现。这与您的第二个选项非常相似吗?@ourlord是的,如果您所说的
foo
函数在后台线程中调用foo\u callin
(听起来可能就是这样,这取决于所称的bind(foo\u callin,…)
),它所做的事情与第二个选项非常相似。哦,是的,我记得bind
函数位于一个自实现async
命名空间下,该命名空间应该包括一个线程/任务调度器或管理器。事情现在变得非常清楚了!再次感谢您的评论!