Возможно ли иметь состояние гонки в питоне aiohttp socketio? - PullRequest
1 голос
/ 23 сентября 2019

Я работаю над кодом, аналогичным приведенному ниже.Иногда программа перестает работать или я получаю странные ошибки, связанные с доступом к сеансу 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)

1 Ответ

2 голосов
/ 23 сентября 2019

Здесь определенно есть проблема:

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 перебирает копию исходного списка.Изменение списка теперь не будет «мешать» копии.

Обратите внимание, однако, что добавления и удаления из списка не распознаются копией.

Итак, короче говоря, ответ на вашвопрос да , но это не имеет ничего общего с async io.Та же проблема может легко возникнуть в синхронном коде:

for i in my_list:
    my_list.remove(1)  # don't do this
...