协程:基于单线程来实现并发 ,又称微程,纤程(Coroutine) python
并发的本质 :切换 + 保存状态 即由用户程序本身控制调度的编程
(1 协程的本质就是在单线程下由用户控制一个任务遇到阻塞就切换到另一个任务执行以此来提高效率多线程
2 python线程属于内核级别的,即由操做系统控制(遇io或时间过长被迫交出cpu执行权限)并发
3 单线程开启协程,一旦遇io就会从应用程序级别(而非操做系统)控制切换,以此来提高效率异步
协程的优势:socket
1 协程的切换开销小,属于程序级别的切换操做系统感知不到异步编程
2 单线程内就能够实现并发的效果,最大限度地利用cpu函数
缺点:spa
1 协程的本质是单线程下,没法利用多核,程序 ——》进程———》》线程————》》协程操作系统
2协程是单个线程,于是一旦协和出现阻塞,将会阻塞整个线程
协程的特色:
1,单线程并发 2修改共享数据不须要加锁 3 在本身的的程序里保存多个控制上下文栈
上下文栈 :(每一次代码执行和函数调用都会产生一个执行环境,称为执行上下文。
一个执行上下文(caller)又能够激活(调用)另外一个执行上下文(callee),这时caller会暂停自身的执行把控制权交给callee进入callee的执行上下文,callee执行完毕后将控制权交回caller,callee能够用return或者抛出Exception来结束本身的执行。)
多个执行上下文会造成执行上下文栈,最顶层是当前执行上下文,底层是全局执行上下文。
切换的状态:1 发生阻塞(IO) 2计算时间过长
对于第二种纯计算的切换则会下降效率:
第一种任务在遇到io切,在利用阻塞的时间完成任务二的计算,效率的提高就在于此
yield 的状态保存属于代码级别的
yield生成器须要初始化一次生成器而后再调用 send /
greenlet模块能够很是简单的实现多个任务直接的切换 ,但当切换任务遇到 io 则原地阻塞仍没有解决
遇到io 自动切换来提高效率的问题
Gevent模块:对于既有计算操做又有阻塞操做能够在执行任务1遇到阻塞就利用阻塞的时间去执行任务2来提高效率 能够实现并发同步或异步编程
用法:
g1=gevent.spawn() #建立一个协程对象g1 ,spawn() 第一个参数是函数名
g2 = gevent.spawn()
g1.join() #等待g1结果
g2.join() #等待g1结果
#或者上述二者合做: gevent.joinall([g1,g2])
g1.value #拿到func1 的返回值
send 能够把一个函数的结果传给另一个函数,以此来实现单线程之间的切换
对于单线程下,咱们不可避免的出现io操做,但若是咱们能在本身的程序中控制单线程下的多个任务
能在一个任务遇到io阻塞时就切换到另一个任务去计算,这样就保证了该线程可以在最大限度下处于
就绪状态(随时均可以被cpu执行的状态)
多线程并发套接字:
服务端:
from gevent import monkey;monkey.patch_all()
import gevent
from multiprocessing import Process
from socket import *
def talk(conn,addr):
while True:
try:
data=conn.recv(1024)
print('%s:%s [%s]' %(addr[0],addr[1],data))
if not data:break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
if __name__ == '__main__':
# server('127.0.0.1',8091)
s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(('127.0.0.1',8090))
s.listen(5)
while True:
conn, addr = s.accept()
print('%s:%s' % (addr[0], addr[1]))
g1 = gevent.spawn(talk, conn, addr)
客户端:
from socket import *c=socket(AF_INET,SOCK_STREAM)c.connect(('127.0.0.1',8090))while True: msg=input('>>: ').strip() if not msg:continue c.send(msg.encode('utf-8')) data=c.recv(1024) print(data.decode('utf-8'))