目录
协程
1.yield的简单实现
2.greenlet 模块
3.gevent模块
协程:协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。
而协程是非抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。
协程主解决的是IO的操作。
协程,又称微线程,纤程。英文名Coroutine。
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
前面我们学习生成器的时候知道,这个只有在调用的时候才执行
import time
def test1():
while True:
print('test1....')
time.sleep(1)
yield
def test2():
while True:
print('test2....')
time.sleep(1)
yield
def main():
g1 = test1()
g2 = test2()
while True:
next(g1)
next(g2)
if __name__ == '__main__':
main()
输出为:
协和调用:
# 使用协程
# import time
#
#
# def test1():
# while True:
# print('test1....')
# time.sleep(1)
# yield
#
#
# def test2():
# while True:
# print('test2....')
# time.sleep(1)
# yield
#
#
# def main():
# g1 = test1()
# g2 = test2()
# while True:
# next(g1)
# next(g2)
#
#
# if __name__ == '__main__':
# main()
import time
def test1():
for i in range(3):
print('test1....',i)
yield
time.sleep(1)
def test2():
for i in range(3):
print('test2....',i)
yield
def main():
g1 = test1()
g2 = test2()
for i in range(3):
next(g1)
next(g2)
print('结束了')
if __name__ == '__main__':
main()
输出为:
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
安装 :pip3 install greenlet。如果引入的时候还是报错,使用pycharm进行下载安装,
选择 file --> settings --> Project:项目名称 --> Project Interpreter-->…
greenlet模块
- gr1=greenlet(目标函数)
- gr1.switch() 切换执行
代码:
from greenlet import greenlet
def test1():
print('test1....01')
gr2.switch()
print('test1....02')
gr2.switch()
def test2():
print('test2....01')
gr1.switch()
print('test2....02')
# 1、将要执行的函数封装到greenlet对象中
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 2、想先执行哪个函数就可以对象.swith()方法进行执行
gr1.switch()
输出为:
使用协程
import greenlet
import time
def test1():
for i in range(5):
print('test1...', i)
g2.switch() # 切换到g2
time.sleep(1)
def test2():
for i in range(5):
print('test2...', i)
g1.switch() # 切换到g1
if __name__ == '__main__':
g1 = greenlet.greenlet(test1)
g2 = greenlet.greenlet(test2)
g1.switch() # 让程序去执行g1
print('程序结束了。。。')
输出为:
import gevent
# 如果程序中没有耗时操作就顺序执行。
def test1():
for i in range(5):
print('test1...', i)
gevent.sleep(1) # 使用耗时模块可以自动操作
def test2():
for i in range(5):
print('test2...', i)
gevent.sleep(1)
if __name__ == '__main__':
g1 = gevent.spawn(test1)
g2 = gevent.spawn(test2)
g1.run()
print('程序结束了')
输出为:
如上代码所示,gevent模块中自带了sleep耗时函数,当使用这个耗时函数时,cpu会跳转到另一个就绪的程序,达到人工设置让其自动切换的功能。
如果需要使用time.sleep()耗时的话,需要打一个补丁
from gevent import monkey
monkey.patch_all()
打完之后就可以实现了
import gevent
import time
from gevent import monkey
monkey.patch_all()
def test1():
for i in range(5):
print('test1...', i)
time.sleep(1)
def test2():
for i in range(5):
print('test2...', i)
time.sleep(1)
if __name__ == '__main__':
g1 = gevent.spawn(test1)
g2 = gevent.spawn(test2)
g1.run()
print('程序结束了')
输出为:
使用joinall可以一次生成多个
import gevent
import time
from gevent import monkey
monkey.patch_all()
def test1():
for i in range(5):
print('test1...',i)
time.sleep(1)
def test2():
for i in range(5):
print('test2...', i)
time.sleep(1)
if __name__ == '__main__':
gevent.joinall([
gevent.spawn(test1),
gevent.spawn(test2)
])
输出和上面一样
import requests
import gevent
def download_img(url, img_name):
response = requests.get(url) # 获取图片中的二进制数据
with open('img/' + img_name, mode='wb') as f:
f.write(response.content)
if __name__ == '__main__':
url='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1548222339580&di=8033e859c88d8de0dd07fb0908a511ab&imgtype=0&src=http%3A%2F%2Fimgm.gmw.cn%2Fattachement%2Fjpg%2Fsite2%2F20170222%2Ff44d307589e71a1729e422.jpg'
url1='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1548222339580&di=d21ac1313363549c11498991e2446e85&imgtype=0&src=http%3A%2F%2Fcn.toluna.com%2Fdpolls_images%2F2017%2F09%2F21%2F371665b2-af55-43b9-a25e-ba30eea4a737_x400.jpg'
gevent.joinall({
gevent.spawn(download_img,url,'panda1.jpg'),
gevent.spawn(download_img,url1,'panda2.jpg')
})
就下载好了: