ThreadPoolExecutor 上 运行 游戏的龙卷风 4.x 解决方案不再有效。需要帮助重构它
Tornado 4.x solution of running game on ThreadPoolExecutor not working anymore. Need help refactoring it
我的 ThreadPoolExecutor/gen.coroutine(tornado v4.x) 绕过阻止网络服务器的解决方案不再适用于 tornado 版本 6.x.
不久前,我开始使用 Tornado 网络服务器 (v4.x) 和 websockets 开发在线浏览器游戏。每当需要用户输入时,游戏都会将问题发送给客户端并等待响应。比起我使用 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 函数会创建一个带有列表(选择)和 id 的字典,并将其存储在玩家 self.callbacks 中。之后它一直停留在 while 循环中,直到 websocket.on_message 函数将收到的答案(看起来像这样:id:Choice_id,例如 1:12838732)放入回调字典中。
WebSocketHandler.write_message
方法不是线程安全的,因此它只能从 IOLoop 的线程中调用,而不能从 ThreadPoolExecutor 中调用(这一直是正确的,但有时它似乎无论如何都可以工作).
修复此代码的最简单方法是将 IOLoop.current()
保存在主线程的全局变量中(current()
函数访问线程局部变量,因此您不能从中调用它线程池)并使用 ioloop.add_callback(self.socket.write_message, message)
(并从 send
中删除 @gen.coroutine
- 如果它们不包含 yield
表达式,则使函数协程没有任何好处)。
我的 ThreadPoolExecutor/gen.coroutine(tornado v4.x) 绕过阻止网络服务器的解决方案不再适用于 tornado 版本 6.x.
不久前,我开始使用 Tornado 网络服务器 (v4.x) 和 websockets 开发在线浏览器游戏。每当需要用户输入时,游戏都会将问题发送给客户端并等待响应。比起我使用 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 函数会创建一个带有列表(选择)和 id 的字典,并将其存储在玩家 self.callbacks 中。之后它一直停留在 while 循环中,直到 websocket.on_message 函数将收到的答案(看起来像这样:id:Choice_id,例如 1:12838732)放入回调字典中。
WebSocketHandler.write_message
方法不是线程安全的,因此它只能从 IOLoop 的线程中调用,而不能从 ThreadPoolExecutor 中调用(这一直是正确的,但有时它似乎无论如何都可以工作).
修复此代码的最简单方法是将 IOLoop.current()
保存在主线程的全局变量中(current()
函数访问线程局部变量,因此您不能从中调用它线程池)并使用 ioloop.add_callback(self.socket.write_message, message)
(并从 send
中删除 @gen.coroutine
- 如果它们不包含 yield
表达式,则使函数协程没有任何好处)。