01同步和异步
生活中常常会遇到在超市排队买东西的情况,排在你前面的人没有结算完成,你就无法付账,在计算机中也有类似的情形,一个程序在执行之前,需要等待其他的程序执行完成,大家还能举出其他的例子吗?
同步
含义:指两个或两个以上随时间变化的量在变化过程中保持一定的相对关系
现象:有一个共同的时钟,按来的顺序一个一个处理
直观感受 :就是需要等候,效率低下
我们用两个函数来模拟两个客户端请求,并依次进行处理:
# coding:utf-8
def req_a():
"""模拟请求a"""
print '开始处理请求req_a'
print '完成处理请求req_a'
def req_b():
"""模拟请求b"""
print '开始处理请求req_b'
print '完成处理请求req_b'
def main():
"""模拟tornado框架,处理两个请求"""
req_a()
req_b()
if __name__ == "__main__":
main()
开始处理请求req_a
完成处理请求req_a
开始处理请求req_b
完成处理请求req_b
同步是按部就班的依次执行,始终按照同一个步调执行,上一个步骤未执行完不会执行下一步。
想一想,如果在处理请求req_a时需要执行一个耗时的工作(如IO),其执行过程如何?
# coding:utf-8
import time
def long_io():
"""模拟耗时IO操作"""
print("开始执行IO操作")
time.sleep(5)
print("完成IO操作")
return "io result"
def req_a():
print("开始处理请求req_a")
ret = long_io()
print("ret: %s" % ret)
print("完成处理请求req_a")
def req_b():
print("开始处理请求req_b")
print("完成处理请求req_b")
def main():
req_a()
req_b()
if __name__ == "__main__":
main()
开始处理请求req_a
开始执行IO操作
完成IO操作
ret: io result
完成处理请求req_a
开始处理请求req_b
完成处理请求req_b
在上面的测试中,我们看到耗时的操作会将代码执行阻塞住,即req_a未处理完req_b是无法执行的。
我们怎么解决耗时操作阻塞代码执行?
异步
对于耗时的过程,我们将其交给别人(如其另外一个线程)去执行,而我们继续往下处理,当别人执行完耗时操作后再将结果反馈给我们,这就是我们所说的异步。
含义 :双方不需要共同的时钟,也就是接收方不知道发送方什么时候发送,所以在发送的信息中就要有提示接收方开始接收的信息,如开始位,同时在结束时有停止位
现象:没有共同的时钟,不考虑顺序来了就处理
直观感受:就是不用等了,效率高
02阻塞和非阻塞
同样是刚才排队的情形,当你在排队的同时,你的状态是怎样的呢?在计算机里面应该怎么描述呢?
阻塞调用
需要等待
含义
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,CPU不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回
现象
读套接字时没有数据等数据来,写套接字时写不下了也一直等,等能写下了往里写(套接字被写满的原因不在本地,在于网络另一头的套接字被写满了来不及读出去,导致本地的套接字内容来发不出去堵住了)
直观感受
执着
非阻塞调用
不需要等待
含义
非阻塞调用是指没有调用结果立即返回,当前线程不被挂起,可以继续做其它工作
现象
读套接字时没有数据,不等直接返回干别的事去,写套接字写不下了也不写了,直接返回干别的事去
直观感受
勤奋
例:去医院
03异步编程
在前面的介绍的基础上,来看下 Tornado 中,当同时有多个请求发送过来时,而且其中还有请求发生阻塞,会产生什么样的后果呢?
因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求。
tornado.httpclient.AsyncHTTPClient
Tornado提供了一个异步Web请求客户端tornado.httpclient.AsyncHTTPClient用来进行异步Web请求。
fetch(request, callback=None)
用于执行一个web请求request,并异步返回一个tornado.httpclient.HTTPResponse响应。
request可以是一个url,也可以是一个tornado.httpclient.HTTPRequest对象。如果是url,fetch会自己构造一个HTTPRequest对象。
HTTPRequest
HTTP请求类,HTTPRequest的构造函数可以接收众多构造参数,最常用的如下:
url (string) – 要访问的url,此参数必传,除此之外均为可选参数
method (string) – HTTP访问方式,如“GET”或“POST”,默认为GET方式
headers (HTTPHeaders or dict) – 附加的HTTP协议头
body – HTTP请求的请求体
HTTPResponse
HTTP响应类,其常用属性如下:
- code: HTTP状态码,如 200 或 404
- reason: 状态码描述信息
- body: 响应体字符串
- error: 异常(可有可无)
通过回调函数来实现异步
import tornado.httpclient
class CallbackHandler(BaseHandler):
"""通过回调函数来实现异步"""
@tornado.web.asynchronous
def get(self):
client = tornado.httpclient.AsyncHTTPClient() # 异步的方法
client.fetch("http://127.0.0.1:8000/sync",callback=self.on_response)
self.write('Ok!'+'<br>')
def on_response(self,response):
self.write(response.body)
print(response)
self.write('---------CallbackHandler---')
self.finish() # 注意一定要加上
tornado.web.asynchronous
此装饰器用于回调形式的异步方法,并且应该仅用于HTTP的方法上(如get、post等)。
此装饰器不会让被装饰的方法变为异步,而只是告诉框架被装饰的方法是异步的,当方法返回时响应尚未完成。只有在request handler调用了finish方法后,才会结束本次请求处理,发送响应。
不带此装饰器的请求在get、post等方法返回时自动完成结束请求处理。
通过协程实现异步
#导入模块
import tornado.gen
class GenHandler(BaseHandler):
"""通过协程实现的异步"""
@tornado.web.asynchronous
@tornado.gen.coroutine # coroutine 协程
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield tornado.gen.Task(client.fetch,"http://127.0.0.1:8000/sync")
self.write(response.body)
print(response)
self.write('---------gen---')
通过协程实现异步(自定义函数)
class FuncHandler(BaseHandler):
"""通过协程实现的异步"""
@tornado.web.asynchronous
@tornado.gen.coroutine # coroutine 协程
def get(self):
response = yield self.func()
print(response)
self.write(response.body)
@tornado.gen.coroutine
def func(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield tornado.gen.Task(client.fetch,"http://127.0.0.1:8000/sync")
raise tornado.gen.Return(response)
通过协程来实现异步(使用requests模块)
第一步:安装模块
pip install futures
pip install requests
第二步:导入模块
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
import requests
通过协程来实现异步(使用requests模块)
class MyFuncHandler(BaseHandler):
"""通过协程实现的异步"""
executor = ThreadPoolExecutor()
@tornado.web.asynchronous
@tornado.gen.coroutine # coroutine 协程
def get(self):
response = yield self.func()
print(response)
self.write(response.text)
@run_on_executor
def func(self):
response = requests.get("http://127.0.0.1:8000/sync")
return response
代码
08aysc.py
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
from tornado.options import define, options
import tornado.websocket
from pycket.session import SessionMixin
define(name='port', default=8080, type=int, help='run port')
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self):
current_user = self.session.get('username')
if current_user:
return current_user
return None
import tornado.httpclient
class Synchandler(BaseHandler):
def get(self, *args, **kwargs):
client = tornado.httpclient.HTTPClient() # tornado单线程 此时用的同步 会阻塞
response = client.fetch('http://127.0.0.1:8000/sync')
print(response)
self.write('-------Synchandler------')
class CallbackHandler(BaseHandler):
"""通过回调函数来实现异步"""
@tornado.web.asynchronous
def get(self, *args, **kwargs):
client = tornado.httpclient.AsyncHTTPClient() # tornado单线程 此时用的异步 不会阻塞
response = client.fetch('http://127.0.0.1:8000/sync', callback=self.on_response) # 模拟发生了阻塞
print(response)
self.write('ok' + '<br>')
def on_response(self, response):
print(response)
self.write('---------CallbackHandler---')
self.finish() # 注意一定要加上
class AbcHandler(BaseHandler):
def get(self, *args, **kwargs):
self.write('abc')
import tornado.gen
class GenHandler(BaseHandler):
"""通过协程实现的异步"""
@tornado.web.asynchronous
@tornado.gen.coroutine # coroutine 协程
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield tornado.gen.Task(client.fetch, "http://127.0.0.1:8000/sync")
self.write(response.body)
print(response)
self.write('---------gen---')
class FuncHandler(BaseHandler):
"""通过协程来实现异步"""
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self, *args, **kwargs):
response = yield self.func()
print(response)
self.write('---------gen---')
@tornado.gen.coroutine
def func(self):
client = tornado.httpclient.AsyncHTTPClient() # tornado单线程 此时用的异步 不会阻塞
response = yield tornado.gen.Task(client.fetch, "http://127.0.0.1:8000/sync")
raise tornado.gen.Return(response)
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
import requests
class MyFuncHandler(BaseHandler):
"""通过协程实现的异步"""
executor = ThreadPoolExecutor()
@tornado.web.asynchronous
@tornado.gen.coroutine # coroutine 协程
def get(self):
response = yield self.func()
print(response)
self.write(response.text)
@run_on_executor
def func(self):
response = requests.get("http://127.0.0.1:8000/sync")
return response
application = tornado.web.Application(
handlers=[
(r'/sync', Synchandler),
(r'/abc', AbcHandler),
(r'/callback', CallbackHandler),
(r'/gen', GenHandler),
(r'/func', FuncHandler),
(r'/myfunc', MyFuncHandler),
],
template_path='template',
login_url='/login',
cookie_secret='R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
static_path='statics',
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost',
'port': 6379,
'db_sessions': 5,
'db_notifications': 11,
'max_connections': 2 ** 31,
},
'cookies': {
'expires_days': 30,
'max_age': 100
},
},
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
008websocket.py
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
from tornado.options import define, options
from tornado.web import authenticated
from data.user_module import User
import tornado.websocket
from pycket.session import SessionMixin
import datetime
import time
define(name='port', default=8000, type=int, help='run port')
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self):
current_user = self.session.get('username')
if current_user:
return current_user
return None
class Synchandler(BaseHandler):
def get(self, *args, **kwargs):
time.sleep(3)
self.write('Synchandler')
application = tornado.web.Application(
handlers=[
(r'/sync', Synchandler),
],
template_path='template',
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost',
'port': 6379,
'db_sessions': 5,
'db_notifications': 11,
'max_connections': 2 ** 31,
},
'cookies': {
'expires_days': 30,
'max_age': 100
},
},
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()