用于阻止ioloop的Python 3.5异步

时间:2015-11-04 13:12:22

标签: python python-3.x async-await python-asyncio aiohttp

我有一个带有两个处理程序的简单aiohttp-server。 第一个在async for循环中进行一些计算。第二个只返回文本响应。 not_so_long_operation以最慢的递归实现返回第30个斐波纳契数,这需要大约一秒钟。

def not_so_long_operation():
    return fib(30)

class arange:
    def __init__(self, n):
        self.n = n
        self.i = 0

    async def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        self.i += 1
        if self.i <= self.n:
            return i
        else:
            raise StopAsyncIteration

# GET /
async def index(request):
    print('request!')
    l = []
    async for i in arange(20):
        print(i)
        l.append(not_so_long_operation())

    return aiohttp.web.Response(text='%d\n' % l[0])

# GET /lol/
async def lol(request):
    print('request!')
    return aiohttp.web.Response(text='just respond\n')

当我尝试抓取/然后/lol/时,只有当第一个完成时,它才会给第二个回复。 我做错了什么以及如何使索引处理程序在每次迭代时释放ioloop?

3 个答案:

答案 0 :(得分:4)

您的示例没有用于在任务之间切换的屈服点await语句)。 异步迭代器允许await / __aiter__中使用__anext__,但不要将其自动插入到您的代码中。

说,

class arange:
    def __init__(self, n):
        self.n = n
        self.i = 0

    async def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        self.i += 1
        if self.i <= self.n:
            await asyncio.sleep(0)  # insert yield point
            return i
        else:
            raise StopAsyncIteration

应该按预期工作。

在实际应用中,您很可能不需要await asyncio.sleep(0)次呼叫,因为您将等待数据库访问和类似活动。

答案 1 :(得分:3)

由于fib(30)受CPU限制并且共享很少数据,因此您应该使用ProcessPoolExecutor(而不是ThreadPoolExecutor):

async def index(request):
    loop = request.app.loop
    executor = request.app["executor"]
    result = await loop.run_in_executor(executor, fib, 30)
    return web.Response(text="%d" % result)
创建executor

设置app

app = Application(...)
app["exector"] = ProcessPoolExector()

答案 2 :(得分:2)

这里不需要异步迭代器。相反,您可以简单地将控件返回到循环内的事件循环。在python 3.4中,这是通过使用简单的11-04 14:21:27.437 1356-1541/me.janvandijk.receptenapp E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #2 Process: me.janvandijk.receptenapp, PID: 1356 java.lang.RuntimeException: An error occured while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:304) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355) at java.util.concurrent.FutureTask.setException(FutureTask.java:222) at java.util.concurrent.FutureTask.run(FutureTask.java:242) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.json.JSONObject.toString()' on a null object reference at me.janvandijk.receptenapp.Recept$retrievedata.doInBackground(Recept.java:62) at me.janvandijk.receptenapp.Recept$retrievedata.doInBackground(Recept.java:54) at android.os.AsyncTask$2.call(AsyncTask.java:292) at java.util.concurrent.FutureTask.run(FutureTask.java:237)         at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)         at java.lang.Thread.run(Thread.java:818)

来完成的
yield

在python 3.5中,你可以定义一个基本上做同样事情的@asyncio.coroutine def index(self): for i in range(20): not_so_long_operation() yield 对象:

Empty

然后将其与class Empty: def __await__(self): yield 语法一起使用:

await

或者只使用asyncio.sleep(0)recently optimized

async def index(request):
    for i in range(20):
        not_so_long_operation()
        await Empty()

您还可以使用default executor

在线程中运行async def index(request): for i in range(20): not_so_long_operation() await asyncio.sleep(0)
not_so_long_operation