Tornado 4.x решение для запуска игры на ThreadPoolExecutor больше не работает. Нужна помощь в рефакторинге - PullRequest
0 голосов
/ 04 апреля 2019

My ThreadPoolExecutor / gen.coroutine (tornado v4.x) решение обойти блокировку веб-сервера больше не работает с торнадо версии 6.x.

Некоторое время назад я начал разрабатывать онлайн-браузерную игру, используявеб-сервер Tornado (v4.x) и веб-сокеты.Всякий раз, когда ожидается ввод данных пользователем, игра отправляет вопрос клиенту и ждет ответа.Тогда я использовал gen.coroutine и ThreadPoolExecutor, чтобы сделать эту задачу неблокируемой.Теперь, когда я начал рефакторинг игры, она не работает с tornado v6.x, и задача снова блокирует сервер.Я искал возможные решения, но до сих пор не смог заставить его работать снова.Мне не ясно, как изменить существующий код, чтобы он снова стал неблокирующим.

server.py:

class PlayerWebSocket(tornado.websocket.WebSocketHandler):
    executor = ThreadPoolExecutor(max_workers=15)
    @run_on_executor
    def on_message(self,message):
        params = message.split(':')
        self.player.callbacks[int(params[0])]=params[1]

if __name__ == '__main__':
    application = Application()
    application.listen(9999)
    tornado.ioloop.IOLoop.instance().start()

player.py:

@gen.coroutine
def send(self, message):
   self.socket.write_message(message)

def create_choice(self, id, choices):
    d = {}
    d['id'] = id
    d['choices']=choices
    self.choice[d['id']]=d
    self.send('update',self)
    while not d['id'] in self.callbacks:
        pass
    del self.choice[d['id']]
    return self.callbacks[d['id']]

Всякий раз, когда необходимо сделать выбор,Функция create_choice создает dict со списком (выбором) и идентификатором и сохраняет его в плеерах self.callbacks.После этого он просто остается в цикле while, пока функция websocket.on_message не поместит полученный ответ (который выглядит следующим образом: id: Choice_id, например, 1: 12838732) в dict обратных вызовов.

1 Ответ

0 голосов
/ 05 апреля 2019

Метод WebSocketHandler.write_message не является потокобезопасным, поэтому его можно вызывать только из потока IOLoop, а не из ThreadPoolExecutor (это всегда было верно, но иногда могло показаться, что все равно работает).

Самый простой способ исправить этот код - сохранить IOLoop.current() в глобальной переменной из основного потока (функция current() обращается к локальной переменной потока, поэтому ее нельзя вызвать из пула потоков) ииспользуйте ioloop.add_callback(self.socket.write_message, message) (и удалите @gen.coroutine из send - делать функции сопрограммы бесполезно, если они не содержат выражений yield).

...