协程
协程
1.切换+保存状态:用yield实现
import time def producer(): g=consumer() next(g) for i in range(1000000): g.send(i) def consumer(): while True: n=yield start=time.time() producer() stop=time.time() print(stop-start)
2.greenlet模块
greenlet模块与yield一样都不能检测io然后自动切换,唯一的优点是比yield的切换方式方便
from greenlet import greenlet def eat(name): print('%s eat 1'%name) p2.switch('alex') print('%s eat 2'%name) p2.switch() def play(name): print('%s play 1'%name) p1.switch() print('%s play 2'%name) p1=greenlet(eat) p2=greenlet(play) p1.switch('alex')
3.协程
(1)协程:单线程下实现并发----遇到io切换,是一种用户态的轻量级线程,即是由用户程序自己控制单线程下任务的切换
(2)特点:
- 只在一个单线程里实现并发
- 修改共享数据不需要加锁
- 用户程序里自己保存多个控制流的上下文栈
- 一个协程遇到IO操作自动切换到其它线程(如何实现检测IO,yield、greenlet都无法实现,就用到gevent模块(select机制))
(3)gevent模块实现协程
from gevent import monkey;monkey.patch_all() from threading import current_thread import gevent,time def eat(): print('%s eat 1'%current_thread().getName()) gevent.sleep(2) print('%s eat 2'%current_thread().getName()) def play(): print('%s play 1'%current_thread().getName()) gevent.sleep(1) print('%s play 2'%current_thread().getName()) start=time.time() g1=gevent.spawn(eat,) g2=gevent.spawn(play,) gevent.joinall([g1,g2]) stop=time.time() print(stop-start)
(4)单线程并发爬页面
from gevent import monkey;monkey.patch_all() import gevent import requests from threading import current_thread def get(url): print('%s get %s' %(current_thread().getName(),url)) response=requests.get(url) if response.status_code == 200: return {'url':len(response.text)} # print({'url':len(response.text)}) g1=gevent.spawn(get,'http://www.baidu.com') g2=gevent.spawn(get,'http://www.python.org') g3=gevent.spawn(get,'http://www.jd.com') g1.join() g2.join() g3.join() print(g1.value) print(g2.value) print(g3.value)
(5)基于协程实现单线程下套接字通信
- 服务端
from gevent import monkey;monkey.patch_all() import gevent from socket import * print('start') def connect(ip,port): server=socket(AF_INET,SOCK_STREAM) server.bind((ip,port)) server.listen(5) while True: conn,addr=server.accept() print('%s:%s'%(addr[0],addr[1])) gevent.spawn(talk,conn,addr) def talk(conn,addr): while True: try: data=conn.recv(1024) conn.send(data.upper()) print('%s:%s [%s]' % (addr[0], addr[1],data)) except Exception: break if __name__ == '__main__': connect('127.0.0.1',8080)
- 客户端
from socket import * from threading import Thread def receive(): s=socket() s.connect(('127.0.0.1',8080)) while True: msg=input('>>:') if not msg:break s.send(msg.strip().encode('utf-8')) data=s.recv(1024) print(data.decode('utf-8')) if __name__ == '__main__': p=Thread(target=receive) p.start()

浙公网安备 33010602011771号