多任务--协程
一、协程
1.概念:协程,又称微线程,纤程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行 通俗理解只 要在def里面只看到一个yield关键字表示就是协程
2.gevent:
模块gevent 中 的monkey 可以破解 time.sleep recv accept 等
使用gevent编写多协程,不使用monkey进行任务切换, 不是多任务的
使用gevent 创建协程 自动运行
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang from gevent import monkey monkey.patch_all() # 破解代码 实现任务切换 默认不能自动切换 import gevent import time # 能够切换的操作: time.sleep() recv accept # 不加monkey 不是多任务 的协程 def worker(no): for i in range(5): print("这是协程 %s %s" % (no, gevent.getcurrent())) time.sleep(1) # gevent.sleep(1) def main(): # 创建协程 自动运行 g1 = gevent.spawn(worker, 1111) g2 = gevent.spawn(worker, 2222) # 阻塞等待协程执行完成 - 主要是让主进程主线程阻塞在这里 # g1.join() # g2.join() gevent.joinall([g1, g2]) if __name__ == '__main__': main() """ 代码结果 """ 这是协程 1111 <Greenlet at 0x211b025c178: worker(1111)> 这是协程 2222 <Greenlet at 0x211b025c340: worker(2222)> 这是协程 1111 <Greenlet at 0x211b025c178: worker(1111)> 这是协程 2222 <Greenlet at 0x211b025c340: worker(2222)>
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import time def work1(): while True: print("-----work1------") yield time.sleep(0.5) def work2(): while True: print("-----work2------") yield time.sleep(0.5) if __name__ == '__main__': # 创建生成器对象 w1 = work1() w2 = work2() while True: next(w1) next(w2) """ 执行结果 """ -----work1------ -----work2------ -----work1------ -----work2------
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang import time import greenlet def work1(): while True: print("-----work1------") time.sleep(0.5) g2.switch() def work2(): while True: print("-----work2------") time.sleep(0.5) g1.switch() if __name__ == '__main__': # 创建生成器对象 g1 = greenlet.greenlet(work1) g2 = greenlet.greenlet(work2) g1.switch() """ 执行结果 """ -----work1------ -----work2------ -----work1------ -----work2------
二、进程、线程、协程的区别和应用场景
1.区别
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
2.应用场景
- 资源消耗关心 多线程或者协程
- 多任务的网络程序 建议优先使用协程
三、案例--gevent多任务下载图片
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang from gevent import monkey # monkey.patch_all() import urllib.request import gevent import time def down_img(url): """下载指定路径的图片 参数就是图片的链接-网址 URL URI""" # # "https://2018/10/15/922c4f46586103d3e4ac76d1930a95b1.png", file_name = url[url.rfind("/") + 1:] print("开始下载图片 %s" % file_name) # 2.响应对象<网页数据> 请求 打开网址 response = urllib.request.urlopen(url) # 3.解析其中数据 bytes data = response.read() # 4.写入文件 with open(file_name, "wb") as file: file.write(data) print("完成下载图片 %s" % file_name) def main(): begin = time.time() # 1.创建协程 img_list = [ "https://rpic.douyucdn.cn/live-cover/roomCover/2018/10/15/922c4f46586103d3e4ac76d1930a95b1.png", "https://rpic.douyucdn.cn/live-cover/roomCover/2018/07/24/f26f76b07852b37430792745a66be94b.jpg" ] g1 = gevent.spawn(down_img, img_list[0]) g2 = gevent.spawn(down_img, img_list[1]) # 2.等待协程执行完成 gevent.joinall([g1, g2]) end = time.time() print("花费%f秒" % (end - begin)) if __name__ == '__main__': main()