同步与异步复习

python实现异步的两种方法

  • 回调函数
  • 协程

使用回调函数实现异步 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
import threading

def task1(callback):
def run(callback):
print('开始耗时操作')
time.sleep(2)
print('结束耗时操作')
callback('hello world')
threading.Thread(target=run,args=(callback,)).start()


def finish(data):
print('开始处理回调函数')
print('接收到task1的响应数据',data)
print('结束处理回调函数')


if __name__ == '__main__':
task1(finish)

使用协程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import time
import threading

global gen

def reqA():
print('reqA start')
res = yield longIO()
print('接受到longIO的响应数据:',res)
print('reqA end')


def reqB():
print('reqB start')
time.sleep(2)
print('reqB end')

def longIO():
def run():
print('start longIO')
time.sleep(2)
try:
print('++++++++')
global gen
gen.send('hello')
except StopIteration as e:
pass
threading.Thread(target=run).start()

def main():
global gen
gen = reqA()
next(gen)

print('****************')
reqB()

main()

# reqA start
# start longIO
# ****************
# reqB start
# ++++++++
# 接受到longIO的响应数据: hello
# reqB end
# reqA end

Tornado中的异步

因为epoll主要是用来解决网络的并发间题,所以 Tornado的异步也是主要体现在网络的异步上,即异步Web请求

1
2
(r'/test10',views.index.TestHandler10),
(r'/test11',views.index.TestHandler11),
1
2
3
4
5
6
7
8
class TestHandler10(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
time.sleep(20)
self.write('test10')

class TestHandler11(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
self.write('test11')

此时先后访问http://127.0.0.1:8000/test10http://127.0.0.1:8000/test11 , 那么,test'11会阻塞20秒,等待test10响应完了才返回结果

  • tornado.httpclient.AsyncHTTPClient : tornado提供的异步Web请求客户端,用来进行异步Web请求

  • fetch(request,callback=None) : 用于执行一个Web请求 , 并异步响应返回一个tornado.httpclient.HTTPResponse

    • request可以是一个url , 也可以是一个Tornado.httpclient.HTTPRequest对象
    • 如果是一个url , 后台会自动转为request对象
  • tornado.web.asynchronous : 不关闭通信的通道

  • HTTPRequest :

    参数 :

    • url : 字符串类型,必传
    • method : 字符串类型
    • headers : 字典或者HTTPHeaders
    • body : HTTP请求体
  • HTTPResponse :
    参数 :

    • code : 状态码
    • readson : 状态码的描述
    • body : 响应的数据
    • error : 异常

回调函数实现异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import json
from tornado.httpclient import AsyncHTTPClient

class TestHandler10(tornado.web.RequestHandler):
def on_response(self,response):
if response.error:
self.send_error(500)
else:
data = json.loads(response.body)
self.write(data)
# 关闭通信通道
self.finish()

# 不关闭通信的通道
@tornado.web.asynchronous
def get(self,*args,**kwargs):
url = 'https://www.baidu.com/'
# 新建异步请求对象
client = AsyncHTTPClient()
# 执行Web请求
client.fetch(url,self.on_response)
self.write('test10')

协程实现异步

1
2
3
4
5
6
7
8
9
10
11
12
class TestHandler11(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self,*args,**kwargs):
url = 'https://www.baidu.com/'
client = AsyncHTTPClient()
res = yield client.fetch(url)

if res.error:
self.send_error(500)
else:
data = json.loads(res.body)
self.write(data)

上面的逻辑比较乱 , 这时就可以分离一下请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TestHandler11(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self,*args,**kwargs):
res = yield self.get_data()
self.write(res)

@tornado.gen.coroutine
def get_data(self,*args,**kwargs):
url = 'https://www.baidu.com/'
client = AsyncHTTPClient()
res = yield client.fetch(url)

if res.error:
ret = {'ret':0}
else:
ret = json.loads(res.body)
# 通过返回错误来实现,类似于生成器里的send
raise tornado.gen.Return(ret)