tornado 4.x 之后,官方搞了自己的协程,弄了一个Future之类的。
以前用他的异步一直也就是弄弄AsyncHTTPClient什么的,甚是浅显。
但是作为一个新时代的四有青年,生在红旗下,长在春风里,怎么能满足于此呢对不对。
于是我定了一个这样的需求:
tornado接受请求 -> 后端消耗若干时间执行任务
要求是接受请求是异步的
查了一些资料写了这样的代码:
main.py
# coding:utf-8
import tornado.ioloop
import tornado.web
from tornado.gen import coroutine
from tornado.web import asynchronous
import tasync
class MainHandler(tornado.web.RequestHandler):
@coroutine
#@asynchronous
def get(self):
print('Hi')
result = yield tasync.async()
self.write("%s" % result )
class MainHandler2(tornado.web.RequestHandler):
def get(self):
self.write('test')
application = tornado.web.Application([
(r"/", MainHandler),
(r"/a", MainHandler2),
])
if __name__ == "__main__":
application.listen(8888)
print('Started.')
tasync.init()
tornado.ioloop.IOLoop.instance().start()
tasync.py
# coding:utf-8
import time
import threading
from functools import partial
from tornado.ioloop import IOLoop
from tornado.concurrent import Future
count = 1
future_dict = {}
def async(*args, **kwargs):
global count
future = Future()
callback = kwargs.pop("callback", None)
if callback:
IOLoop.instance().add_future(future, lambda future: callback(future.result()))
future_dict[count] = [future, time.time()]
count += 1
return future
def init():
interval = 0.3
io_loop = IOLoop.instance()
def the_loop():
while True:
t = time.time()
for k, v in future_dict.items():
if t - v[1] >= 5:
io_loop.add_callback(_on_result, k, v[0])
del future_dict[k]
time.sleep(interval)
threading.Thread(target=the_loop, args=()).start()
def _on_result(result, future):
print(result, time.time())
future.set_result(result)
上面俩文件复制下来就可以跑,py2/3皆可。
这个代码做了这样一件事情:
我的代码有两个url,/ 是我预设的异步点, /a 是一个调试页面。
访问/ 的话,程序应该在5秒钟后返回。
程序分为两个线程:tornado主线程,任务轮询线程(完成任务后给ioloop发消息)
但问题是这样的:
访问/,立即访问/a : /页面在等待状态,/a页面响应,正常。
访问/,立即再开另一页面访问/,第二张页面居然被阻塞了!
你们看一下日志就明白:
Started.
Hi
1436887952.72
Hi
1436887958.91
Hi是 / 接受请求,输出时间是回调完成。
我表示非常不解,求解惑。
PS: 我知道单以这个需求而论,tornado-celery是可用的,但是tcelery满足不了我其他的需求,所以不考虑。另外就主要是知其然知其所以然了。
1
wy315700 2015-07-15 00:07:21 +08:00
time.sleep
是阻塞的,,所以 被阻塞了。 tornado的异步要求很高的,,, |
3
alphonsez 2015-07-15 00:57:41 +08:00
result = yield tasync.async()
这个变成coroutine了吧,所以你的print是在async task执行完成才执行的。 |
4
alphonsez 2015-07-15 01:01:57 +08:00
你要直接async,应该是这样的:
@asynchronous def get(self): print('Hi') result = tasync.async() self.write("%s" % result ) return result 不过这个result是一个future. 如果你要print那个真正的result,肯定得等5秒过后啊。本来的coroutine写法相当于js里的(宽恕我不怎么懂python吧……): def get(self): print('Hi') return tasync.async().then(function(result) { self.write("%s" % result ); return result; }); |
5
fy OP |
7
zeayes 2015-07-15 10:02:27 +08:00
tornado的异步是针对网络io阻塞情况下的异步。
|
8
janxin 2015-07-15 12:15:25 +08:00
如果你需要单个的等待,可以使用gen.sleep来解决你的需求
|
9
fy OP |
10
mulog 2015-07-15 12:48:42 +08:00 1
哦呵呵呵 tornado 躺着中枪
你用的是浏览器吧?你试试用两个不同的浏览器访问,或者直接开两个 terminal 用 curl 访问 毫无问题 |
11
fy OP @mulog
哦草日了头像了……原来是浏览器的锅。万万没想到啊!那么事实上我的代码已经做好异步了…… Started. Hi Hi (1, 1436935932.168) (2, 1436935933.073) |
12
alphonsez 2015-07-15 16:08:01 +08:00
yield不就是等结果吗?没有堵住啊。你的thread应该是闲着的啊
|
14
7harryprince 2015-08-05 10:11:32 +08:00
这么写简便多了,赞
|