为什么要用协程,异步?
异步非阻塞、asyncio
tornado、fastapi、djando 3.x asgi 、aiohttp 都在异步 ->提升性能
使用协程和异步编程有以下几个主要原因:
协程能做什么?
协程是一种轻量级的并发编程方式,可以用于处理各种任务。以下是协程的一些常见应用场景:
协程不是计算机提供的,程序员人为创造的(被称为微线程)
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术,简而言之,其实就是通过一个线程实现代码块相互切换执行。
实现协程有这几种方法:
from greenlet import greenlet(pip3 install greenlet)
def func1():
print(1) # 第一步:输出 1
gr2.switch() # 第三步:切换到func2函数
print(2) # 第6步:输出2
gr2.switch() # 第七步:切换到func2函数,从上一次执行的位置继续向后执行
def func2():
print(3) # 第四步:输出3
gr1.switch() # 第五步:切换到func1函数,从上一次的执行的位置继续向后执行
print(4) # 第八步:输出4
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第一步:去执行 func1函数
def func1()
yield 1
yield from func2
yield 2
def func2()
yield 3
yield 4
f1 = func1()
for item in f1:
print(item)
python3.4以后的版本可用 遇到IO阻塞自动切换
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
async def func2():
print(3)
await asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
在一个线程中如果遇到IO等待的时间,线程不会傻傻的等待,而是利用空闲的时候再去干点其他的任务。
并发、并行
同步、异步
阻塞、非阻塞
阻塞是指调用函数时候当前线程被挂起
非阻塞是指调用函数时候当前线程不会被挂起,而是立即返回
同步和异步
同步,就是在发出一个调用时,在没有得到结果之前, 该调用就不返回。换句话说就是调用者主动接收这个结果。
异步,就是调用者发出一个调用后,不用等结果,直接可以进行下一步。也就是说这个调用没有返回结果,是被调用者通过状态、通知来通知调用者,或者通过回调函数处理这个调用。
通俗点说:
同步就像是正在苦苦追求一个女生的男生,这天他向这个女生表白,女生要给他一个是否同意交往的回答,女生没有回答之前他会一直等这个结果。
异步就像是个海王,广撒网,精准捕捞。群发表白消息,不管第一个女生是否给了回答,反正就是给列表里所有的女生都发了表白信息。等有女生同意和他交往,他就收到了想要的答案(返回值)。
阻塞非阻塞
阻塞和非阻塞关注得是程序在等待调用结果(消息,返回值)时的状态
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
还是上面那个例子,你向一个女生表白,然后她如果一直吊着你不给你答复(绿茶),你为了她茶饭不思,这就形成了阻塞。
如果你是海王,这个女生没有给你答复,你可以转过头去看看其他女生是否给你反馈,只要时不时回来看看这个女生有没有返回什么信息即可,这就是非阻塞。
PS:这部分内容学起来还真的是挺绕的...
再简单点理解就是:
1.同步,就是我调用一个功能,该功能没有结束前,我死等结果
2.异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)
3.阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回
4.非阻塞,就是调用我(函数),我(函数)立即返回,通过select调用通知者
同步IO和异步IO的区别在于:数据拷贝时进程是否阻塞
阻塞IO和非阻塞IO的区别在于:应用程序的调用结果是否立即返回
IO分两阶段:
1.数据准备阶段
2.内核空间复制回用户进程缓冲区阶段
阻塞IO:给女神发一条短信, 说我来找你了, 然后就默默的一直等着女神下楼, 这个期间除了等待你不会做其他事情。
非阻塞IO:给女神发短信, 如果不回, 接着再发, 一直发到女神下楼, 这个期间你除了发短信等待不会做其他事情。
IO多路复用:是找一个宿管大妈来帮你监视下楼的女生, 这个期间你可以些其他的事情. 例如可以顺便看看其他妹子,玩玩王者荣耀, 上个厕所等等. IO复用又包括 select, poll, epoll 模式. 那么它们的区别是什么?
1) select大妈 每一个女生下楼, select大妈都不知道这个是不是你的女神, 她需要一个一个询问, 并且select大妈能力还有限, 最多一次帮你监视1024个妹子。
2) poll大妈不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神。
3) epoll大妈不限制盯着女生的数量, 并且也不需要一个一个去问. 那么如何做呢? epoll大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, epoll大妈就知道这个是不是你女神了, 然后大妈再通知你。
上面这四种情况都是同步IO,它们有一个共同点就是, 当女神走出宿舍门口的时候, 你已经站在宿舍门口等着女神了。
异步IO的情况:
你告诉女神我来了, 然后你就去打游戏了, 一直到女神下楼了, 发现找不见你了, 女神再给你打电话通知你, 说我下楼了, 你在哪呢? 这时候你才来到宿舍门口。
next()
send()
throw()
close()
##### yield 和 yield from的区别
def g1(iterable):
yield iterable
def g2(iterable):
yield from iterable
for value1 in g1(range(10)):
print(value1)
for value2 in g2(range(10)):
print(value2)
# python3.3中 新加了 yield from语法
from itertools import chain
my_list = [1, 2, 3]
my_dict = {
"bobby1": "http://projectsedu.com",
"bobby2": "http://www.imoc.com"
}
# 使用yield
def my_chain(*args, **kwargs):
for my_iterable in args:
for value in my_iterable:
yield value
# 使用 yield from
def my_chain(*args, **kwargs):
for my_iterable in args:
yield from my_iterable
for value in my_chain(my_list, my_dict, range(5)):
print(value)