在 Python Tornado Web 服务器中使用协程
Working with coroutines in Python Tornado Web Server
我正在为具有 Python 2x 的网络浏览器游戏开发自动驾驶汽车。我使用 Tornado Web 服务器在本地主机上 运行 游戏,我 post 并在名为 "FrameHandler" 的函数中使用 JSON 数据格式从游戏接收数据,我还确定了行为汽车应该在 "to_dict_faster()" 函数中。
在这里,我的问题是我可以在协同程序的帮助下,在特定时间间隔内将数据写入保存在 speed_data 变量中的文本文件。但是,我无法转储 JSON 数据以在此特定时间间隔内运行,因为 "FrameHandler" 的行为类似于 While True 并且它始终请求转储数据。我想做的是发送所需的行为,如在特定时间间隔内写入文本文件,同时不更改流帧处理程序,因为它会影响游戏的 FPS。
我想弄清楚我怎样才能长时间这样做,如果有任何帮助,这里将是很好的:
@gen.coroutine
def sampler():
io_loop = tornado.ioloop.IOLoop.current()
start = time.time()
while True:
with open("Sampled_Speed.txt", "a") as text_file:
text_file.write("%d,%.2f\n" % (speed_data, ((time.time() - start))))
yield gen.Task(io_loop.add_timeout, io_loop.time() + period)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.redirect("/static/v2.curves.html")
class FrameHandler(tornado.web.RequestHandler):
def post(self):
global speed_data
data = json.loads(self.get_arguments("telemetry")[0])
ar = np.fromstring(base64.decodestring(self.request.body), dtype=np.uint8)
image = ar.reshape(hp.INPUT_SIZE, hp.INPUT_SIZE, hp.NUM_CHANNELS)
left, right, faster, slower = data["action"]
terminal, action, all_data, was_start = (
data["terminal"],
Action(left=left, right=right, faster=faster, slower=slower),
data["all_data"],
data["was_start"]
)
for i in range(len(all_data)):
data_dict=all_data[i]
speed_data = data_dict[u'speed']
position_data=data_dict[u'position']
result_action = agent.steps(image, 0.1, terminal, was_start, action, all_data)
if speed_data < 4000:
self.write(json.dumps(result_action.to_dict_faster()))
else:
self.write(json.dumps(result_action.to_dict_constant()))
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/frame", FrameHandler),
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": static_path})
], debug=True)
if __name__ == "__main__":
app = make_app()
if "SERVER_PORT" in os.environ:
port = int(os.environ["SERVER_PORT"])
else:
port = 8880
print "LISTENING ON PORT: %d" % port
app.listen(port)
tornado.ioloop.IOLoop.current().run_sync(sampler)
tornado.ioloop.IOLoop.current().start()
您可以将文件写入移动到不同的线程(例如使用 tornado 的 run_on_executor),因此 python 解释器将自动从 Sampler
切换到主线程 FrameHandler
在写。但是你必须使用 thread-safe speed_data
变量,我以 stdlib Queue.Queue
为例:
class Handler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
global speed_data
speed_data.put("REALLY BIG TEST DATA\n")
self.finish("OK")
class Sampler():
executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
def __init__(self, queue):
self._q = queue
@run_on_executor
def write_sample(self):
with open("foobar.txt", "w") as f:
while True:
data = self._q.get()
f.write(data)
if __name__ == '__main__':
application = Application(
[("/status", Handler)]
)
server = HTTPServer(application)
server.listen(8888)
speed_data = Queue.Queue()
smp = Sampler(speed_data)
IOLoop.current().add_callback(smp.write_sample)
IOLoop.current().start()
我正在为具有 Python 2x 的网络浏览器游戏开发自动驾驶汽车。我使用 Tornado Web 服务器在本地主机上 运行 游戏,我 post 并在名为 "FrameHandler" 的函数中使用 JSON 数据格式从游戏接收数据,我还确定了行为汽车应该在 "to_dict_faster()" 函数中。
在这里,我的问题是我可以在协同程序的帮助下,在特定时间间隔内将数据写入保存在 speed_data 变量中的文本文件。但是,我无法转储 JSON 数据以在此特定时间间隔内运行,因为 "FrameHandler" 的行为类似于 While True 并且它始终请求转储数据。我想做的是发送所需的行为,如在特定时间间隔内写入文本文件,同时不更改流帧处理程序,因为它会影响游戏的 FPS。
我想弄清楚我怎样才能长时间这样做,如果有任何帮助,这里将是很好的:
@gen.coroutine
def sampler():
io_loop = tornado.ioloop.IOLoop.current()
start = time.time()
while True:
with open("Sampled_Speed.txt", "a") as text_file:
text_file.write("%d,%.2f\n" % (speed_data, ((time.time() - start))))
yield gen.Task(io_loop.add_timeout, io_loop.time() + period)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.redirect("/static/v2.curves.html")
class FrameHandler(tornado.web.RequestHandler):
def post(self):
global speed_data
data = json.loads(self.get_arguments("telemetry")[0])
ar = np.fromstring(base64.decodestring(self.request.body), dtype=np.uint8)
image = ar.reshape(hp.INPUT_SIZE, hp.INPUT_SIZE, hp.NUM_CHANNELS)
left, right, faster, slower = data["action"]
terminal, action, all_data, was_start = (
data["terminal"],
Action(left=left, right=right, faster=faster, slower=slower),
data["all_data"],
data["was_start"]
)
for i in range(len(all_data)):
data_dict=all_data[i]
speed_data = data_dict[u'speed']
position_data=data_dict[u'position']
result_action = agent.steps(image, 0.1, terminal, was_start, action, all_data)
if speed_data < 4000:
self.write(json.dumps(result_action.to_dict_faster()))
else:
self.write(json.dumps(result_action.to_dict_constant()))
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/frame", FrameHandler),
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": static_path})
], debug=True)
if __name__ == "__main__":
app = make_app()
if "SERVER_PORT" in os.environ:
port = int(os.environ["SERVER_PORT"])
else:
port = 8880
print "LISTENING ON PORT: %d" % port
app.listen(port)
tornado.ioloop.IOLoop.current().run_sync(sampler)
tornado.ioloop.IOLoop.current().start()
您可以将文件写入移动到不同的线程(例如使用 tornado 的 run_on_executor),因此 python 解释器将自动从 Sampler
切换到主线程 FrameHandler
在写。但是你必须使用 thread-safe speed_data
变量,我以 stdlib Queue.Queue
为例:
class Handler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
global speed_data
speed_data.put("REALLY BIG TEST DATA\n")
self.finish("OK")
class Sampler():
executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
def __init__(self, queue):
self._q = queue
@run_on_executor
def write_sample(self):
with open("foobar.txt", "w") as f:
while True:
data = self._q.get()
f.write(data)
if __name__ == '__main__':
application = Application(
[("/status", Handler)]
)
server = HTTPServer(application)
server.listen(8888)
speed_data = Queue.Queue()
smp = Sampler(speed_data)
IOLoop.current().add_callback(smp.write_sample)
IOLoop.current().start()