1

我正在处理类似于以下代码的代码。有时程序停止工作,或者我收到有关 socketio 会话访问的奇怪错误。慢慢地我觉得这可能是比赛条件。

它的更多伪代码。我想证明,我从多个协程访问全局共享状态和 socketio 会话。

import asyncio as aio
from aiohttp import web
import socketio


app = web.Application()
sio = socketio.AsyncServer()

app["sockets"] = []

@sio.on("connect")
async def connect(sid):
    app["sockets"].append(sid)

@sio.on("disconnect")
async def disconnect(sid):
    app["sockets"].remove(sid)

@sio.on("set session")
async def set_session(sid, arg):
    await sio.save_session(sid, {"arg": arg})

async def session_route(req):
    data = await req.json()
    for sid in app["sockets"]:
        await sio.save_session(sid, {"arg": data["arg"]})
    return web.Response(status=200)

if __name__ == '__main__':
    web.run_app(app)
4

1 回答 1

2

这里肯定有问题:

for sid in app["sockets"]:  # you are iterating over a list here
    await sio.save_session(...)  # your coroutine will yield here

您正在迭代列表app["sockets"],并且在每次迭代中都使用await关键字。当使用await关键字时,您的协程将被暂停,事件循环检查是否可以执行或恢复另一个协程。

假设connect(...)协程在session_route等待时运行。

app["sockets"].append(sid)  # this changed the structure of the list

connect(...)改变了列表的结构。这会使该列表当前存在的所有迭代器无效。协程也是如此disconnect(...)

所以要么不要修改列表,要么至少不要在列表更改后重用迭代器。后一种解决方案在这里更容易实现:

for sid in list(app["sockets"]):
    await sio.save_session(...)

现在 for 循环遍历原始列表的副本。现在更改列表不会“干扰”副本。

但是请注意,副本无法识别列表中的添加和删除。

所以,简而言之,你的问题的答案是yes,但这与 async io 无关。同步代码中很容易出现同样的问题:

for i in my_list:
    my_list.remove(1)  # don't do this
于 2019-09-23T06:49:34.957 回答