一:协程
概念:
协程:
1)并发:JMeter并发100个请求。
2)并行:2个进程分别在2个cpu上并行运行
3)同步:IO操作,耗时,等待操作完毕
4)异步:不等待操作完毕
5)并发是实现异步
实现异步(并发)的方法有:多线程、协程
(1)多线程:CPU调度多个线程 (由内核决定的)
(2)协程:开发人员调度多个任务(开发人员:指的是用户)
举栗:有两个函数 work1() ,work2(),当work1阻塞的时候运行work2,work2阻塞的时候运行work1
要实现这个例子:需要有 1)挂起当前状态(暂停) 2)激活挂起的状态(恢复) 的能力
核心思想:
协程的核心思想:
为了实现异步io
若干个协程任务,当某个任务遇到阻塞时,自动切换到非阻塞的任务上
阻塞: io阻塞。如:input("请输入一个数: "),磁盘io(写数据是需要时间的),网络io(网络请求数据时需要时间的)
用户态:切换不需要CPU调度
核心态:线程切换,进程切换,核心态, 切换需要cpu调度
二。从生成器到协程:python在3.5版本之前是没有协程这个概念的,生产器是在python2.3版本中引入的,使用生成器实现协程是在 python2.5 可以进行的。
发展历程:
从生成器到协程:从Python2到Python3,协程经历了翻天覆地的变化。
简单的生成器:
https:www.python.org/dev/peps/pep-0255/
Python2.3中,加入了新的关键字:yield
在PE255中,引入了yield表达式
规定yield语句只能在函数中使用。包含yield语句的函数被称为生成器函数
当执行到yield语句时,函数的状态会被冻结(挂起所有状态,如:局部变量、指令指针、堆栈状态等),以便下载
next时恢复状态继续执行。
通过生成器实现协程:
协程的底层架构是在PEP 342中定义,在Python2.5实现的。
实现思想:使用yield挂起生成器,使用send方法激活生成器就具备实现协程功能
执行generator.send(None) 完全等同于调用生成器的 next方法。使用其他参数调用send也有同样的效果, 的是,当前生成器表达式产生的值会不一样。
参考资料:PEP 342 Coroutines via Enhanced Generators https://www.python.org/dev/peps/pep-0342/
协程的演变:
Python3.3增加了 yield from 语法,使用调用嵌套生成器变得简单
举栗:
def f():
yield from [1,2,3]
Python3.5 加入了关键字 async 和 await,将生成器和协程全部分开
参考资料:
PEP 380 Syntax for Delegating to a Subgenerator https://www.python.org/dev/peps/pep-0380/
PEP 525 Asynchronous Generators https://www.python.org/dev/peps/pep-0525/
生成器实现协程:
"""
也就是使用生成器实现多任务
"""
import time
def work1():
for i in range(5):
print("work1:函数运行中")
time.sleep(1)
yield
def work2():
for i in range(5):
print("work2:函数运行中")
time.sleep(1)
yield
def calc_time(func):
def wrap(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(f"耗时{end - start:.2f}秒")
return wrap
@calc_time
def main():
g1 = work1()
g2 = work2()
while True:
try:
next(g1) #使用迭代控制实现协程
next(g2)
except StopIteration:
break
if __name__ == '__main__':
main()
运行结果:
二。gevent
实现方法一:通过gevent.time()实现协程
"""
也就是使用生成器实现多任务:
注意:
1.gevent 如果不适用阻塞,主程序结束,子程序也会结束(子程序==线程)
"""
#1,gevent 协程方法一,使用gevent.time()实现协程
import time
import gevent
def work1():
for i in range(5):
print("work1:函数运行中")
gevent.sleep(1)
def work2():
for i in range(5):
print("work2:函数运行中")
gevent.sleep(1)
def calc_time(func):
def wrap(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(f"耗时{end - start:.2f}秒")
return wrap
@calc_time
def main():
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)
g1.join()
g2.join()
if __name__ == '__main__':
main()
运行结果:
实现方法二:通过打补丁的方式实现协程(导入monkey模块中的patch_all()方法)
"""
也就是使用生成器实现多任务:
注意:
1.gevent 如果不适用阻塞,主程序结束,子程序也会结束(子程序==线程)
"""
#1,gevent 协程方法一,使用gevent.time()实现协程
import time
import gevent
from gevent import monkey
monkey.patch_all()
#2.gevent,协程使用方法二 打补丁,导入monkey模块中的patch_all()方法 。导入: from gevent import monkey; monkey.patch_all()
def work3():
for i in range(5):
print("work3:函数运行中")
time.sleep(1)
def work4():
for i in range(5):
print("work4:函数运行中")
time.sleep(1)
def calc_time(func):
def wrap(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(f"耗时{end - start:.2f}秒")
return wrap
@calc_time
def main2():
g3 = gevent.spawn(work3)
g4 = gevent.spawn(work4)
g3.join()
g4.join()
if __name__ == '__main__':
main2()
运行结果:
三。asycio
"""
python3.5之后可用
"""
import time
import asyncio #异步IO库,单线程实现并发 --协程
#async 关键字包裹 await 关键字结束
async def work1():
for i in range(5):
print(f"work1:函数运行")
await asyncio.sleep(1)
async def work2():
for i in range(5):
print(f"work2:函数运行")
await asyncio.sleep(1)
def calc_time(func):
def wrap(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(f"耗时{end - start:.2f}秒")
return wrap
async def main():
task1 = asyncio.create_task(work1()) #任务一
task2 = asyncio.create_task(work2()) # 任务二
await task1
await task2
if __name__ == '__main__':
start = time.time()
asyncio.run(main())
end = time.time()
print(f"耗时{end - start:.2f}秒")
运行结果: