Python协程
一:介绍
# 协程主要解决的是IO操作,并行计算效果也不是很好
协程:又名纤程、微线程。协程是用户态的轻量级线程,完全由用户控制调度。另外,它只支持单线程下的并发
使用方式: 从python单线程内开启协程,但与python原生线程不同的是:一旦遇到IO,就会从应用程序层面(而非操作系统)控制切换,以此提高效率(非IO操作切换与效率无关)
操作系统控制线程的切换和协程的切换:
优点:
# 非抢占式运行,不像线程和进程那样争夺运行权
# 协程的切换开销比操作系统控制的线程小,操作系统还检测不到,因为属于程序级别的切换
# 修改共享数据不需要加锁,判断状态即可
# 没有锁
缺点:
# 协程无法利用多核,因为本质是单线程,需要每个进程内开启多线程,每个线程开启协程。但可以利用 多进程+协程(解决并发的一种好方案) 利用多核
# 协程出现阻塞,将会阻塞整个线程
import time def parasite(name): print('吃包子了') while True: bum = yield print('[%s]吃了编号[-%s-]的包子' % (name, bum)) def chef(): n = 0 p1 = parasite('TOM') p2 = parasite('jack') next(p1) next(p2) while True: time.sleep(1) print('%s编号的包子做好了' % n) p1.send(n) p2.send(n) n += 1 chef()
第二:Greenlet 实现
greenlet(run=None, parent=None): 创建一个greenlet实例.
gr.parent:每一个协程都有一个父协程,当前协程结束后会回到父协程中执行,该 属性默认是创建该协程的协程.
gr.run: 该属性是协程实际运行的代码. run方法结束了,那么该协程也就结束了
gr.switch(*args, **kwargs): 切换另一层协程
gr.throw(): 切换到另一层协程,接着抛出一个异常
Greenlet 是C语言实现的,python自带的yield不能随意切换,但Greenlet是可以的
缺点:遇到IO会被阻塞
from greenlet import greenlet def a(): print(1) b.switch() # 2.切换到b print(3) b.switch() # 4.切换到b def b(): print(2) a.switch() # 3.切换到 a print(4) a = greenlet(a) b = greenlet(b) a.switch() # 1.切换到A函数执行
第三:Gevent的实现方式
import gevent,time from gevent.queue import Queue def a(): for i in range(10): print("a函数在生成编号[-%s-]的包子" % i) q.put(i) gevent.sleep(1) # 阻塞后,立即切换 def b(): for i in range(10): res = q.get() print("b函数在吃编号[-%s-]的包子" % res) gevent.sleep(1) # 阻塞后,立即切换 q = Queue() gevent.joinall( [ gevent.spawn(a), gevent.spawn(b), ] )
# 由此得知,gevent 比greenlet更好,因为遇到阻塞会自动切换
过程图:
识别其他类型的阻塞:
from gevent import monkey;monkey.patch_all() # 这个补丁要打在最前面,或者#coding=utf-8 的后面 import gevent,time from gevent.queue import Queue def a(): for i in range(10): print("a函数在生成编号[-%s-]的包子" % i) q.put(i) time.sleep(1) # 阻塞后,立即切换 def b(): for i in range(10): res = q.get() print("b函数在吃编号[-%s-]的包子" % res) time.sleep(1) # 阻塞后,立即切换 q = Queue() gevent.joinall( [ gevent.spawn(a), gevent.spawn(b), ] )
from gevent import monkey;monkey.patch_all() import gevent from socket import * def server(): s = socket(AF_INET, SOCK_STREAM) s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) s.bind(('127.0.0.1', 888)) s.listen(5) while True: conn, addr = s.accept() gevent.spawn(task, conn, addr) def task(conn, addr): while True: data = conn.recv(1024) print('收到消息:', data) q = '收到你发来的消息'.encode('utf-8') conn.send(q) if __name__ == '__main__': server()
from socket import * c = socket(AF_INET, SOCK_STREAM) c.connect(('127.0.0.1', 888)) while True: data = input('输入发送的消息:').encode('utf-8') c.send(data) msg = c.recv(1024) print(msg.decode('utf8'))


浙公网安备 33010602011771号