Asynchronous programming with Tornado
Asynchronous programming can be tricky for beginners, therefore I think it’s useful to iron some basic concepts to avoid common pitfalls. For an explanation about generic asynchronous programming, I recommend you one of the many resourcesonline. I will focus solely on asynchronous programming in Tornado.
From Tornado’s homepage:
FriendFeed’s web server is a relatively simple, non-blocking web server written in Python. The FriendFeed application is written using a web framework that looks a bit like web.py or Google’s webapp, but with additional tools and optimizations to take advantage of the non-blocking web server and tools. Tornado is an open source version of this web server and some of the tools we use most often at FriendFeed. The framework is distinct from most mainstream web server frameworks (and certainly most Python frameworks) because it is non-blocking and reasonably fast. Because it is non-blocking and uses epoll or kqueue, it can handle thousands of simultaneous standing connections, which means the framework is ideal for real-time web services. We built the web server specifically to handle FriendFeed’s real-time features every active user of FriendFeed maintains an open connection to the FriendFeed servers. (For more information on scaling servers to support thousands of clients, see The C10K problem.)
The first step as a beginner is to figure out if you really need to go asynchronous. Asynchronous programming is more complicated that synchronous programming, because, as someone described, it does not fit human brain nicely.
You should use asynchronous programming when your application needs to monitor some resources and react to changes in their state. For example, a web server sitting idle until a request arrives through a socket is an ideal candidate. Or an application that has to execute tasks periodically or delay their execution after some time. The alternative is to use multiple threads (or processes) to control multiple tasks and this model becomes quickly complicated.
The second step is to figure out if you can go asynchronous. Unfortunately in Tornado, not all the tasks can be executed asynchronously.
Tornado is single threaded (in its common usage, although it supports multiple threads in advanced configurations), therefore any “blocking” task will block the whole server. This means that a blocking task will not allow the framework to pick the next task waiting to be processed. The selection of tasks is done by the IOLoop
, which, as everything else, runs in the only available thread.
For example, this is a wrong way of using IOLoop
:
import time | |
from tornado.ioloop import IOLoop | |
from tornado import gen | |
def my_function(callback): | |
print 'do some work' | |
# Note: this line will block! | |
time.sleep(1) | |
callback(123) | |
@gen.engine | |
def f(): | |
print 'start' | |
# Call my_function and return here as soon as "callback" is called. | |
# "result" is whatever argument was passed to "callback" in "my_function". | |
result = yield gen.Task(my_function) | |
print 'result is', result | |
IOLoop.instance().stop() | |
if __name__ == "__main__": | |
f() | |
IOLoop.instance().start() |
Note that blocking_call
is called correctly, but, being blocking (time.sleep
blocks!), it will prevent the execution of the following task (the second call to the same function). Only when the first call will end, the second will be called by IOLoop
. Therefore, the output in console is sequential (“sleeping”, “awake!”, “sleeping”, “awake!”).
Compare the same “algorithm”, but using an “asynchronous version” of time.sleep
, i.e. add_timeout
:
# Example of non-blocking sleep. | |
import time | |
from tornado.ioloop import IOLoop | |
from tornado import gen | |
@gen.engine | |
def f(): | |
print 'sleeping' | |
yield gen.Task(IOLoop.instance().add_timeout, time.time() + 1) | |
print 'awake!' | |
if __name__ == "__main__": | |
# Note that now code is executed "concurrently" | |
IOLoop.instance().add_callback(f) | |
IOLoop.instance().add_callback(f) | |
IOLoop.instance().start() |
In this case, the first task will be called, it will print “sleeping” and then it will ask IOLoop
to schedule the execution of the rest of the routine after 1 second. IOLoop
, having the control again, will fire the second call the function, which will print “sleeping” again and return control to IOLoop
. After 1 second IOLoop
will carry on where he left with the first function and “awake” will be printed. Finally, the second “awake” will be printed, too. So, the sequence of prints will be: “sleeping”, “sleeping”, “awake!”, “awake!”. The two function calls have been executed concurrently (not in parallel, though!).
So, I hear you asking, “how do I create functions that can be executed asynchronously”? In Tornado, every function that has a “callback” argument can be used with gen.engine.Task
. Beware though: being able to use Task
does not make the execution asynchronous! There is no magic going on: the function is simply scheduled to execution, executed and whatever is passed tocallback
will become the return value of Task
. See below:
import time | |
from tornado.ioloop import IOLoop | |
from tornado import gen | |
def my_function(callback): | |
print 'do some work' | |
# Note: this line will block! | |
time.sleep(1) | |
callback(123) | |
@gen.engine | |
def f(): | |
print 'start' | |
# Call my_function and return here as soon as "callback" is called. | |
# "result" is whatever argument was passed to "callback" in "my_function". | |
result = yield gen.Task(my_function) | |
print 'result is', result | |
IOLoop.instance().stop() | |
if __name__ == "__main__": | |
f() | |
IOLoop.instance().start() |
Most beginners expect to be able to just write: Task(my_func)
, and automagically execute my_func
asynchronously. This is not how Tornado works. This is how Go works! And this is my last remark:
In a function that is going to be used “asynchronously”, only asynchronous libraries should be used.
By this, I mean that blocking calls like time.sleep
or urllib2.urlopen
or db.query
will need to be substituted by their equivalent asynchronous version. For example, IOLoop.add_timeout
instead of time.sleep
, AsyncHTTPClient.fetch
instead of urllib2.urlopen
etc. For DB queries, the situation is more complicated and specific asynchronous drivers to talk to the DB are needed. For example: Motor for MongoDB.
Asynchronous programming with Tornado的更多相关文章
- Async/Await - Best Practices in Asynchronous Programming
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx Figure 1 Summary of Asynchronous Programming ...
- Async/Await - Best Practices in Asynchronous Programming z
These days there’s a wealth of information about the new async and await support in the Microsoft .N ...
- .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)
本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...
- HttpWebRequest - Asynchronous Programming Model/Task.Factory.FromAsyc
Posted by Shiv Kumar on 23rd February, 2011 The Asynchronous Programming Model (or APM) has been aro ...
- Parallel Programming AND Asynchronous Programming
https://blogs.oracle.com/dave/ Java Memory Model...and the pragmatics of itAleksey Shipilevaleksey.s ...
- Asynchronous Programming Patterns
Asynchronous Programming Patterns The .NET Framework provides three patterns for performing asynchro ...
- C#的多线程——使用async和await来完成异步编程(Asynchronous Programming with async and await)
https://msdn.microsoft.com/zh-cn/library/mt674882.aspx 侵删 更新于:2015年6月20日 欲获得最新的Visual Studio 2017 RC ...
- Asynchronous programming with async and await (C#)
Asynchronous Programming with async and await (C#) | Microsoft Docs https://docs.microsoft.com/en-us ...
- .Net Core自实现CLR异步编程模式(Asynchronous programming patterns)
最近在看一个线程框架,对.Net的异步编程模型很感兴趣,所以在这里实现CLR定义的异步编程模型,在CLR里有三种异步模式如下,如果不了解的可以详细看MSDN 文档Asynchronous progra ...
随机推荐
- django页面导出excel
from django.http import HttpResponse from xlwt import * from io import BytesIO def excel_export(requ ...
- Windows 7 64bit Python 2 Install
安装 setuptools 出现 UnicodeDecodeError 文件 Lib/mimetypes.py 中的 bug, 通过以下 patch 修复: Index: Lib/mimetypes. ...
- 批处理设置IP地址 - imsoft.cnblogs
批处理设置IP地址 不知朋友们是否有这样的经历,把本本带到单位上网时,由于单位需要配固定IP地址,而家里是自动获得IP地址的,所以每天都要对这个IP地址设置来设置去,那么有没有简单方便的办法呢?其实我 ...
- python之concurrent.futures模块
一.concurrent.futures模块简介 concurrent.futures 模块提供了并发执行调用的高级接口 并发可以使用threads执行,使用ThreadPoolExecutor 或 ...
- CH3602 Counting Swaps
题意 3602 Counting Swaps 0x30「数学知识」例题 背景 https://ipsc.ksp.sk/2016/real/problems/c.html Just like yeste ...
- wpf的datepicker处理(转)
如果有2个datepicker,控制时间起和止的话,可以把第二个datepicker加一个属性,DisplayDateStart = "{Binding SelectedDate,Eleme ...
- 剑指offer-特定二维数组中查找一个元素是否存在-二分搜索-二维数组
int [][] array ={ {1,2,8,9}, {2,4,9,12}, {4,7,10,13}, {6,8,11,19} }; 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都 ...
- 实习第二天-String对象的不可变性-未解决
public class Reverse { public static void main(String[] args) { String c1=new String("abc" ...
- MVC思想-程序的控制流程-Struts2和SpringMVC黑马流程图
1.初探 javaEE就是搞清前后台是怎么交互的,而控制那个交互的就被称为是:C:控制器 C负责协调调度程序如何执行的,M负责读数据的处理,比如说:验证输入的密码是否正确,是否 有这个权限.V就简单了 ...
- 几本不错的graphql 电子书
当前专门讲graphql 的数据不是很多,但是越来越多的graphql 项目的出现以及graphql 自身的 便捷,老外已经有人去写graphql 的设计以及基本使用了. ebooks 地址 http ...