同步、异步
- 同步:是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
- 异步:是指代码调用IO操作时,不必等IO操作完成就返回的调用方式
阻塞、非阻塞
- 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
- 非阻塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
1、生成器-send方法
send方法有一个参数,该参数指定的是上一次被挂起的yield语句的返回值
'''
生成器的send方法
'''
def create_num(num):
#生成器的send方法
a, b = 0, 1
current_num = 0
while current_num < num:
result = yield a # result = yield a = 'juran'
print('result-->',result)
a, b = b, a+b
current_num += 1
return 'campo'
if __name__ == '__main__':
g = create_num(5)
#for循环可以取出生成器的值
# for i in g:
# print(i)
# #next(g)也可以取出生成器的值
# try:
# while True:
# print(next(g))
# except Exception as e:
# print('==:', e)
#
#
# #send方法启动生成器,第一次调用send必须带参数“None”
print(g.send(None))
print(g.send('juran'))
print(g.send('amy'))
# g.close() 方法关闭生成器,后面不能再调用生成器
g.close()
print(g.send('jerry'))
上面程序的返回值是:其中包含报错信息,因为调用了 g.close() 关闭了生成器,所以,再次 g.send(‘jerry’) 会报错。
2、使用yield完成多任务
'''
使用yeild完成多任务
'''
import time
def task1():
while True:
print('--1--')
time.sleep(0.1)
yield
def task2():
while True:
print('--2--')
time.sleep(0.1)
yield
def main():
t1 = task1()
t2 = task2()
while True:
next(t1) #输出 --1-- 到yield 等待,执行 next(t2)
next(t2) #输出 --2-- 到yield 等待,返回到 while True: 继续循环执行 next(t1)
if __name__ == '__main__':
main()
上面代码返回值,交替返回1 和 2
3、yield from介绍
3.1 python3.3新加了yield from语法
lis = [1, 3, 5]
dic = {
'name':'juran',
'age':18
}
def my_chain(*args, **kwargs):
for my_iterable in args:
# for value in my_iterable:
# yield value
#yeild from 代替了上面两行 for 循环,可以得到同样的结果
yield from my_iterable
for value in my_chain(lis, dic, range(5, 10)):
print(value)
上面代码返回结果如下:
3.2 yield 和 yield from 对比
3.2.1 yield 和 yield from 简单对比
def generator_1(lis):
yield lis
def generator_2(lis):
yield from lis
for i in generator_1(lis):
print('yield 返回值:',end='')
print(i)
print('yield from 返回值:')
for i in generator_2(lis):
print(i)
上面代码返回结果:yield 返回的是lis列表,yield from 返回的是单独的1,2,3的值,就像是把 lis 拆包了一样。
3.2.1 yield 和 yield from 案例分析
【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。
'''子生成器'''
def generator_1():
total = 0
while True:
x = yield
print('加', x)
if not x:
break
total += x
return total
'''委托生成器'''
def generator_2():
while True:
# 建立调用方和字生成器的通信
total = yield from generator_1()
print('加和总数是:', total)
def main():
'''g1调用方'''
# g1 = generator_1()
# g1.send(None)
# g1.send(2)
# g1.send(3)
# g1.send(None)
'''g2调用方'''
g2 = generator_2()
g2.send(None)
g2.send(2)
g2.send(3)
g2.send(None)
if __name__ == '__main__':
main()
上面代码直接调用 g1 会报错:
上面代码委托生成器 g2 调用子生成器 g1 返回结果:g2 默认处理了g1返回的错误
4、协程
协程,又称微线程
协程是python中另外一种实现多任务的方式,只不过比线程更小占用更少执行单元(理解为需要的资源)
Python中的协程大概经历了如下三个阶段:
- 最初的生成器变形yield/send
- yield from
- 在最近的Python3.5版本中引入async/await关键字
4.1 使用greenlet完成多任务
安装模块:pip3 install greenlet
from greenlet import greenlet
import time
def demo1():
while True:
print('demo1')
gr2.switch()
time.sleep(0.5)
def demo2():
while True:
print('demo2')
gr1.switch()
time.sleep(0.5)
if __name__ == '__main__':
gr1 = greenlet(demo1)
gr2 = greenlet(demo2)
gr1.switch()
上面程序返回值:demo1 和 demo2 交替打印
4.2 使用gevent完成多任务
安装模块:pip3 install gevent
简单总结
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源很最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
import gevent
import time
from gevent import monkey
# 将程序中用到的耗时操作 换为gevent中实现的模块
monkey.patch_all()
def fn1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep()
def fn2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep()
def fn3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep()
def main():
# global g1, g2, g3
g1 = gevent.spawn(fn1, 3)
g2 = gevent.spawn(fn2, 5)
g3 = gevent.spawn(fn3, 7)
g1.join()
g2.join()
g3.join()
if __name__ == '__main__':
main()
上面程序返回值是: