协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。
一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
#1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)#2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)
缺点:
#1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程#2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
协程特点:
必须在只有一个单线程里实现并发修改共享数据不需加锁用户程序里自己保存多个控制流的上下文栈附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
greenlet模块
属于第三方模块需要安装,pip3 install greenlet
# from greenlet import greenlet# import time# def eat(name):# print('%s eat 1'%name)# time.sleep(100) #遇到阻塞不会切换# g2.switch('egon')# print('%s eat 2' % name)# g2.switch()### def play(name):# print('%s play 1'%name)# g1.switch()# print('%s play 2' % name)## g1=greenlet(eat)# g2=greenlet(play)## g1.switch('egon')
gevent模块
gevent模块本质用的是greenlet,比他多了个i/o监测功能
import gevent,timedef eat(name): print('%s eat 1'%name) gevent.sleep(3) #此时是不识别time.sleep的 print('%s eat 2' % name)def play(name): print('%s play 1'%name) gevent.sleep(2) print('%s play 2' % name)start=time.time()g1=gevent.spawn(eat,'egon')g2=gevent.spawn(play,'alex')# gevent.sleep(10)# g1.join()# g2.join()gevent.joinall([g1,g2]) print(time.time()-start) #3.0121257305145264
让它识别time等指令
from gevent import monkey;monkey.patch_all() #打补丁,实现gevent模块识别time等i/0,而切换,必须顶格写import gevent,timedef eat(name): print('%s eat 1'%name) time.sleep(3) print('%s eat 2' % name)def play(name): print('%s play 1'%name) time.sleep(2) print('%s play 2' % name)start=time.time()g1=gevent.spawn(eat,'egon')g2=gevent.spawn(play,'alex')gevent.joinall([g1,g2]) print(time.time()-start)
通过gevent实现单线程下的socket并发
from gevent import monkey;monkey.patch_all()from socket import *import gevent#如果不想用money.patch_all()打补丁,可以用gevent自带的socket# from gevent import socket# s=socket.socket()def server(server_ip,port): s=socket(AF_INET,SOCK_STREAM) s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind((server_ip,port)) s.listen(5) while True: conn,addr=s.accept() gevent.spawn(talk,conn,addr)def talk(conn,addr): try: while True: res=conn.recv(1024) print('client %s:%s msg: %s' %(addr[0],addr[1],res)) conn.send(res.upper()) except Exception as e: print(e) finally: conn.close()if __name__ == '__main__': server('127.0.0.1',8080)
from socket import *client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8080))while True: msg=input('>>: ').strip() if not msg:continue client.send(msg.encode('utf-8')) msg=client.recv(1024) print(msg.decode('utf-8'))
from threading import Threadfrom socket import *import threadingdef client(server_ip,port): c=socket(AF_INET,SOCK_STREAM) #套接字对象一定要加到函数内,即局部名称空间内,放在函数外则被所有线程共享,则大家公用一个套接字对象,那么客户端端口永远一样了 c.connect((server_ip,port)) count=0 while True: c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8')) msg=c.recv(1024) print(msg.decode('utf-8')) count+=1if __name__ == '__main__': for i in range(500): t=Thread(target=client,args=('127.0.0.1',8080)) t.start()
posted on 2018-08-10 11:06 阅读( ...) 评论( ...)