目录
协程:协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。而协程是非抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。协程主解决的是IO的操作。
协程,又称微线程,纤程。英文名Coroutine。
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能
一、yield的简单实现
import time
def test1():
while True:
print('test1......')
time.sleep(0.1)
yield
def test2():
while True:
print('test2......')
time.sleep(0.1)
yield
def main():
g1=test1()
g2=test2()
while True:
next(g1)
next(g2)
if __name__ == '__main__':
main()
运行结果:
test1......
test2......
test1......
test2......
......
二、greenlet 模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
安装 :pip3 install greenlet。如果引入的时候还是报错,使用pycharm进行下载安装,
选择 file --> settings --> Project:项目名称 --> Project Interpreter-->…
greenlet模块
- gr1=greenlet(目标函数)
- gr1.switch() 切换执行
创建简单的协程程序
import greenlet,time
def test1():
for i in range(3):
print('test1...',i)
time.sleep(0.5)
g2.switch()
def test2():
for i in range(3):
print("test2...",i)
time.sleep(0.5)
g1.switch()
if __name__ == '__main__':
g1=greenlet.greenlet(test1) # 指定g1执行的目标
g2=greenlet.greenlet(test2)
g1.switch() # 让程序去g1指定的目标去执行
print('程序结束了...')
运行结果·:
test1... 0
test2... 0
test1... 1
test2... 1
test1... 2
test2... 2
程序结束了...
三、gevent模块
import gevent,time
from gevent import monkey
# 打补丁
monkey.patch_all()
# 如果程序中没有耗时操作就顺序执行
def test1():
for i in range(3):
print('test1....',i)
time.sleep(0.5) # 注意事项:需要使用gevent.sleep()耗时操作才能自动切换
def test2():
for i in range(3):
print('test2....',i)
time.sleep(0.5)
if __name__ == '__main__':
g1=gevent.spawn(test1) # 指定执行目标
g2=gevent.spawn(test2)
# 等待g1,g2指定的目标执行完后程序才往下执行
g1.join()
g2.join()
print('程序结束了....')
运行结果:
test1.... 0
test2.... 0
test1.... 1
test2.... 1
test1.... 2
test2.... 2
程序结束了....
gevent.joinall()方法
import gevent,time
from gevent import monkey
# 打补丁
monkey.patch_all()
# 如果程序中没有耗时操作就顺序执行
def test1():
for i in range(2):
print('test1....',i)
time.sleep(0.5)
def test2():
for i in range(2):
print('test2....',i)
time.sleep(0.5)
if __name__ == '__main__':
# 简化写法,当joinall中的内容全部执行完后程序继续往下执行
gevent.joinall([
gevent.spawn(test1), # 指定执行目标
gevent.spawn(test2)
# 等待g1,g2指定的目标执行完后程序才往下执行
])
print('程序结束了....')
运行结果;
test1.... 0
test2.... 0
test1.... 1
test2.... 1
程序结束了....
练习:编写程序实现图片的拷贝
首先引进requests模块
首先点击 file-->setting-->project.day22-多任务(文件所在根目录)---->project Interpreter
找到requests模块,然后下载
应用该模块
开始编写程序:
# 初始requests模块
import requests,gevent
# 给图片具体地址赋值变量
ur1= 'https://b-ssl.duitang.com/uploads/item/201612/10/20161210005234_hZzLn.jpeg'
# 获取该图片地址
response=requests.get(ur1)
# response.content # 获取图片的二进制数据
# 将该图片以二进制形式写进一个新的图片文件
with open('img/1.jpeg',mode='wb') as f:
f.write(response.content)
运行结果:
使用gevent模块同时拷贝两张图片
import requests,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://b-ssl.duitang.com/uploads/item/201612/10/20161210005234_hZzLn.jpeg'
url1='http://img.netbian.com/file/2017/0427/ae20084a88ce11e47eb267acc53f86e5.jpg'
# download_img(url,'mao.jpeg')
# download_img(url1,'yanlingji.jpeg')
gevent.joinall([
gevent.spawn(download_img,url,'mao.jpeg'),
gevent.spawn(download_img,url1,'yanlingji.jpeg')
])
运行结果: