Tornado được thiết kế để chạy tất cả các hoạt động của bạn trong một chuỗi duy nhất, nhưng sử dụng I/O không đồng bộ để tránh bị chặn càng nhiều càng tốt. Nếu DB bạn đang sử dụng có các ràng buộc Python không đồng bộ (những cái lý tưởng cho Tornado cụ thể, như Motor cho MongoDB hoặc momoko cho Postgres), thì bạn sẽ có thể chạy truy vấn DB mà không chặn máy chủ; không cần quy trình hoặc chủ đề riêng biệt.
Để giải quyết ví dụ chính xác mà bạn đã cung cấp, nơi time.sleep(1)
được gọi, bạn có thể sử dụng phương pháp này để làm điều đó không đồng bộ qua coroutines cơn lốc xoáy:
#!/usr/bin/python
import tornado.web
from tornado.ioloop import IOLoop
from tornado import gen
import time
@gen.coroutine
def async_sleep(seconds):
yield gen.Task(IOLoop.instance().add_timeout, time.time() + seconds)
class TestHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
for i in xrange(100):
print i
yield async_sleep(1)
self.write(str(i))
self.finish()
application = tornado.web.Application([
(r"/test", TestHandler),
])
application.listen(9999)
IOLoop.instance().start()
Phần thú vị là async_sleep
. Phương thức đó đang tạo một Tác vụ không đồng bộ, đang gọi phương thức ioloop.add_timeout
. add_timeout
sẽ chạy một cuộc gọi lại cụ thể sau một số giây nhất định, mà không chặn ioloop trong khi chờ thời gian chờ hết hạn. Hãng này hy vọng hai đối số:
add_timeout(deadline, callback) # deadline is the number of seconds to wait, callback is the method to call after deadline.
Như bạn có thể thấy trong ví dụ trên, chúng tôi chỉ thực sự cung cấp một tham số để add_timeout
một cách rõ ràng trong các mã, có nghĩa là chúng tôi kết thúc này này:
add_timeout(time.time() + seconds, ???)
Chúng tôi không cung cấp tham số gọi lại dự kiến. Thực tế, khi gen.Task
thực hiện add_timeout
, nó sẽ thêm đối số từ khóa callback
vào cuối các tham số được cung cấp rõ ràng. Vì vậy, đây:
yield gen.Task(loop.add_timeout, time.time() + seconds)
Kết quả trong việc này được thực hiện bên trong gen.Task():
loop.add_timeout(time.time() + seconds, callback=gen.Callback(some_unique_key))
Khi gen.Callback
được thực hiện sau thời gian chờ, nó báo hiệu rằng gen.Task
hoàn tất, và thực hiện chương trình sẽ tiếp tục sang dòng tiếp theo. Dòng chảy này rất khó để hiểu đầy đủ, ít nhất là lúc đầu (chắc chắn là đối với tôi khi lần đầu tiên tôi đọc về nó). Có thể sẽ hữu ích khi đọc qua số Tornado gen module documentation một vài lần.