1. 想学asyncio,得先了解协程
携程的意义:
计算型的操作,利用协程来回切换执行,没有任何意义,来回切换并保存状态 反倒会降低性能。
IO型的操作,利用协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能,从而实现异步编程(不等待任务结束就可以去执行其他代码)
2. 协程和多线程之间的共同点和区别:
共同点:
都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行;
不同点:
多线程,是在I/O阻塞时通过切换线程来达到并发的效果,在什么情况下做线程切换是由操作系统来决定的,开发者不用操心,但会造成竞争条件 (race condition) ;
协程,只有一个线程,在I/O阻塞时通过在线程内切换任务来达到并发的效果,在什么情况下做任务切换是开发者决定的,不会有竞争条件 (race condition) 的情况;多线程的线程切换比协程的任务切换开销更大;
对于开发者而言,多线程并发的代码比协程并发的更容易书写。
一般情况下协程并发的处理效率比多线程并发更高。
3. greenlet实现协程
greenlet用于创建协程,switch用于进行协程之间的切换某个协程在执行的过程中可以随时的被其他协程通过switch函数来打断,转而去执行其他协程,当前协程的中断现场会被保留,一旦中断的协程再次获得cpu的执行权首先会恢复现场然后从中断处继续执行这种机制下的协程是同步,不能并发
pip install greenlet
import time
import greenlet
def func1():
print("func11")
gr2.switch()
time.sleep(1)
print("func22")
gr2.switch()
def func2():
print("func33")
gr1.switch()
time.sleep(1)
print("func44")
start = time.time()
gr1 = greenlet.greenlet(func1)
gr2 = greenlet.greenlet(func2)
gr1.switch()
end = time.time()
print(end - start)
4. yield关键字实现协程
def func1():
yield 1
yield from func2()
yield 3
def func2():
yield 2
yield 4
ff = func1()
for item in ff:
print(item)
5. (1)gevent实现协程
pip install gevent
from greenlet import greenlet
from time import sleep
def func1():
print("协程1")
sleep(2)
g2.switch()
print("协程1恢复运行")
def func2():
print("协程2")
sleep(1)
g3.switch()
def func3():
print("协程3")
sleep(1)
g1.switch()
if __name__ == '__main__':
# 使用greenlet来创建三个协程
g1 = greenlet(func1)
g2 = greenlet(func2)
g3 = greenlet(func3)
# print(g1)
g1.switch() # 让协程g1取抢占cpu资源
(2) gevent实现异步协程
# 协程被创建出来以后默认是多个协程同步执行
# 我们可以加入monkey补丁,把同步的协程转成异步协程
from gevent import monkey # 注意:monkey的引入必须在其他模块之前
monkey.patch_all() # 用monkey给整个协程队列,添加一个非阻塞I/O的补丁,使得他们成为异步协程
import time
import requests
import gevent
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
def func(url, i):
print("协程%d开启!" % i)
res = requests.get(url=url, headers=headers)
html = res.text
print("协程%d执行结束,获取到的响应体大小为:%d" % (i, len(html)))
if __name__ == '__main__':
start = time.time()
urls = [
"https://www.baidu.com/",
"https://www.qq.com/",
"https://www.sina.com.cn",
"https://www.ifeng.com/",
"https://www.163.com/"
]
# 创建5个协程分别对上面5个网站进行访问
g_list = []
for i in range(len(urls)):
g = gevent.spawn(func, urls[i], i)
g_list.append(g)
# func(urls[i], i)
gevent.joinall(g_list)
end = time.time()
print(end - start)
5. asyncio模块实现异步协程
在python3.4及之后的版本使用,asyncio厉害之处在于:遇到IO操作时会自动切换执行其它任务
import time
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(1) # 遇到IO耗时操作,自动切换到tasks中的其它任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(1) # 遇到IO耗时操作,自动切换到tasks中的其它任务
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(end - start
6. 未完待续...
总结:
在程序中只要看到async
和await
关键字,其内部就是基于协程实现的异步编程,这种异步编程是通过一个线程在IO等待时间去执行其他任务,从而实现并发。
如果是 I/O 密集型,且 I/O 请求比较耗时的话,使用协程。
如果是 I/O 密集型,且 I/O 请求比较快的话,使用多线程。
如果是 计算 密集型,考虑可以使用多核 CPU,使用多进程。