我正在处理类似于以下代码的代码。有时程序会停止运行,或者我会收到有关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)
答案 0 :(得分: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循环遍历原始列表的副本。现在更改列表将不会“干扰”副本。
但是请注意,副本无法识别列表中的添加和删除。
因此,简而言之,您的问题的答案是是,但与异步io无关。同步代码中很容易发生相同的问题:
for i in my_list:
my_list.remove(1) # don't do this