先说说基本概念,什么是同步异步
同步:
所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。
异步:
将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。
同步,是所有的操作都做完,才返回给用户结果。即写完数据库之后,在相应用户,用户体验不好。
异步,不用等所有操作等做完,就相应用户请求。即先相应用户请求,然后慢慢去写数据库,用户体验较好。
举些栗子:
代码一
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 同步程序示例
"""
def reqA():
print('开始执行reqA')
print('结束执行reqA')
def reqB():
print('开始执行reqB')
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
这个同步代码会输出:
开始执行reqA
结束执行reqA
开始执行reqB
结束执行reqB
代码二
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 异步示例-通过回调函数实现
"""
import time
import threading
def longIO():
def run():
print('开始执行耗时任务')
time.sleep(5)
print('结束耗时任务')
threading.Thread(target=run).start()
def reqA():
print('开始执行reqA')
longIO()
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
这个异步代码会输出:
开始执行reqA
开始执行耗时任务
结束执行reqA
开始执行reqB
结束执行reqB
结束耗时任务
说说差别
当上面的代码一中可以体现同步的思想,但是当我们在reqA中加入耗时任务时,整个程序会通过先执行reqA的代码,执行完后再继续执行reqB的代码,对于用户体验是相当的差,我们可以修改下代码,看看实现效果:
代码三
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 同步示例2
"""
import time
def reqA():
print('开始执行reqA')
time.sleep(5)
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
将会输出:
开始执行reqA
等待5秒
结束执行reqA
开始执行reqB
等待2秒
结束执行reqB
总共需要7秒
回看下代码二,我们可以发现代码二需要执行浪费程度远远小于代码三。 这也很好的说明同步与异步的优劣
怎样获取异步函数返回的结果呢
同步获取的方法相当简单并感觉到羞耻:
代码四
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 同步示例2
"""
import time
def longIO():
print('开始执行耗时任务')
time.sleep(5)
print('结束耗时任务')
return "zhang"
def reqA():
print('开始执行reqA')
res = longIO()
print('接收到的值:', res)
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
执行程序不统计其他,花费了7秒的时间
那么同步的获取返回数据该怎么修改呢?
代码五
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 异步示例-通过回调函数实现
"""
import time
import threading
def longIO(callback):
def run(cb):
print('开始执行耗时任务')
time.sleep(5)
print('结束耗时任务')
cb('zhang')
threading.Thread(target=run, args=(callback,)).start()
def func(data):
print('执行回调函数')
print('接收到的值:', data)
print('结束回调函数')
def reqA():
print('开始执行reqA')
longIO(func)
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
开始执行reqA
开始执行耗时任务
结束执行reqA
开始执行reqB
结束执行reqB
结束耗时任务
执行回调函数
接收到的值: zhang
结束回调函数
这个程序化肥了5秒时间,并通过回调函数方式实现获取返回值
除了通过回调的方法,我们还可以通过协程进行实现异步
协程方法实现异步
代码六
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 异步示例-通过回调函数实现
"""
import time
import threading
gen = None
def longIO():
def run():
print('开始执行耗时任务')
time.sleep(5)
try:
global gen
gen.send('zhang')
except:
pass
print('结束耗时任务')
threading.Thread(target=run).start()
def reqA():
print('开始执行reqA')
res = yield longIO()
print('接收到的值:', res)
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
global gen
gen = reqA()
next(gen)
reqB()
if __name__ == '__main__':
main()
开始执行reqA
开始执行耗时任务
开始执行reqB
结束执行reqB
接收到的值: zhang
结束执行reqA
结束耗时任务
代码六存在两个问题?
(1)reqA不能想reqB那样调用,只能作为生成器
(2)多了个gen全局变量问题
优化代码六
代码七
# -*- coding: utf-8 -*-
"""
:author: zhangfq
:order: 异步示例-通过回调函数实现
"""
import time
import threading
def longIO():
print('开始执行耗时任务')
time.sleep(5)
print('结束耗时任务')
yield 'zhang'
def genfunc(func):
def wrapper(*args, **kwargs):
gen1 = func(*args, **kwargs)
gen2 = next(gen1)
def run(g):
res = next(g)
try:
gen1.send(res)
except:
pass
threading.Thread(target=run, args=(gen2,)).start()
return wrapper
@genfunc
def reqA():
print('开始执行reqA')
res = yield longIO()
print('接收到的值:', res)
print('结束执行reqA')
def reqB():
print('开始执行reqB')
time.sleep(2)
print('结束执行reqB')
def main():
reqA()
reqB()
if __name__ == '__main__':
main()
开始执行reqA
开始执行耗时任务
开始执行reqB
结束执行reqB
结束耗时任务
接收到的值: zhang
结束执行reqA