python基础(协程创建,greenlet模块,gevent模块)
一、协程的概述
1.1 定义:
1. 单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程
2. 线程属于内核级别的,即由操作系统控制调度,协程属于应用程序级别(而非操作系统)控制切换
1.2 协程的本质:
在一个线程中,多个任务遇到IO操作时,相互借切换轮流使用cpu,减小开销,增加执行效率
多个协程的本质是一条线程(单线程),所以多个协程不能利用多核
1.3 比较:
IO密集型应用: 多进程->多线程->事件驱动->协程
CPU密集型应用: 多进程-->多线
二、 yield的协程机制
生产者与消费者模型示例:
# 程序之间的相互转换调用-->协程 # 消费者 def consumer(): while True: n = yield # 每次获取一个值,挂起 print(n) # 生产者 def producer(): g = consumer() next(g) for i in range(100): g.send(i) # 每次传递一个值给到consumer # 并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行 # 调用生产者函数,模拟协程 producer()
三、协程模块(greenlet)
示例:
# 协程模块: import time from greenlet import greenlet # 协程模块 def eat(): print('start eating') g2.switch() # 调用sleep方法,切换至sleep函数中执行 time.sleep(1) print('end eating') g2.switch() def sleep(): print('start sleeping') g1.switch() # 调用eat方法,切换至eat函数中执行 time.sleep(1) print('end sleeping') g1 = greenlet(eat) # 实例化g1对象 g2 = greenlet(sleep) # 实例化g2对象 g1.switch() # 调用eat方法,切换至eat函数中执行
四、协程模块(gevent)
# 方式一: import gevent def eat(): # 协程任务 协程函数 print('start eating') # 模块封装的方法(gevent.sleep()):能够阻塞模拟IO操作,切换任务 gevent.sleep(1) # 同time.sleep() print('end eating') def sleep(): # 协程任务 协程函数 print('start sleeping') gevent.sleep(1) print('end sleeping') g1 = gevent.spawn(eat) g2 = gevent.spawn(sleep) # g1.join() # 阻塞,直到g1任务执行完毕 # g2.join() # 阻塞,直到g2任务执行完毕 gevent.joinall([g1, g2]) # g1.join() + g2.join()
# 方式二: # 请求网页 url_dic = { '协程': 'http://www.cnblogs.com/Eva-J/articles/8324673.html', '线程': 'http://www.cnblogs.com/Eva-J/articles/8306047.html', '目录': 'https://www.cnblogs.com/Eva-J/p/7277026.html', '百度': 'http://www.baidu.com', 'sogou': 'http://www.sogou.com', '4399': 'http://www.4399.com', '豆瓣': 'http://www.douban.com', 'sina': 'http://www.sina.com.cn', '淘宝': 'http://www.taobao.com', 'JD': 'http://www.JD.com' } from gevent import monkey; monkey.patch_all() # 由monkey包装后的模块或者包都能识别程序的IO操作 import time import gevent from urllib.request import urlopen # 获取网页 def get_html(name, url): ret = urlopen(url) # 获取网页内容 content = ret.read() # 解码html网页 with open(name, 'wb') as f: # 写入文件 f.write(content) # 同步请求网页: start = time.time() for name in url_dic: get_html(name + '_sync.html', url_dic[name]) ret = time.time() - start print('同步时间 :', ret) # 同步时间 : 3.444990396499634 # 异步协程方式获取网页: start = time.time() g_l = [] for name in url_dic: g = gevent.spawn(get_html, name + '_async.html', url_dic[name]) # 实例化一个协程对象 g_l.append(g) gevent.joinall(g_l) # 阻塞,等待所有任务执行完成 ret = time.time() - start print('异步时间 :', ret) # 异步时间 : 1.6289100646972656
五、协程实现高并发socket_server服务器
import socket import gevent from gevent import monkey monkey.patch_all() # 所有模块打补丁 sk = socket.socket() sk.bind(('127.0.0.1', 9001)) sk.listen() # 消息处理 def talk(conn): while True: msg = conn.recv(1024).decode('utf-8') conn.send(msg.upper().encode('utf-8')) while True: conn, _ = sk.accept() gevent.spawn(talk, conn) # 总结: # 进程 高计算型的场景下使用进程 # 线程 对于IO操作的检测是更加全面且灵敏的 # 协程 能够检测到的io操作是有限的
import socket from threading import Thread def client(i): sk = socket.socket() sk.connect(('127.0.0.1', 9001)) while True: sk.send(b'hello') print(sk.recv(1024)) # 创建500个线程 for i in range(500): Thread(target=client, args=(i,)).start()
https://www.cnblogs.com/WiseAdministrator/