1、基础
# 进程 启动多个进程 进程之间是由操作系统(时间片轮转)负责调用 # 线程 启动多个线程 真正被CPU执行的最小单位是线程# 开启一个线程 创建一个线程 寄存器 堆栈# 关闭一个线程 等都需要时间 # 协程:本质上是一个线程# 能够在多个任务之间切换来节省一些IO时间# 协程切换也需要时间,但是远远小于线程进程开销时间 # 实现并发的手段
2 协程应用背景
import time
'''1'''
def consumer(): # 生成器函数print(123)while True:x=yieldprint('**',x)
c=consumer() # c为生成器,此时函数并没有执行
next(c)
c.send(1)
程序执行顺序:
程序运行结果:
3 在一个程序上进行两个任务的切换
'''2 在一个程序上进行两个任务的切换'''
def consumer(): # 生成器函数while True:x=yieldtime.sleep(1)print('处理了数据',x)
def producer():c=consumer()next(c)for i in range(3):time.sleep(1)print('生产了数据:',i)c.send(i)
producer()
程序执行顺序:
程序运行结果:
4、协程切换:切换后接着执行-greenlet模块
'''3 协程切换:切换后接着执行'''
from greenlet import greenlet
# 真正的协程模块就是使用greenlet完成的切换
def eat():print('eating start')g2.switch()print('eating end')g2.switch()
def play():print('playing start')g1.switch()print('playing end')
g1=greenlet(eat)
g2=greenlet(play)
g1.switch()
程序执行顺序:
程序执行结果:
5 协程切换 gevent
# spawn(func) 大量生产 # join() # .value 得到方法的返回值
例子1:
import geventdef eat():print('eating start')gevent.sleep(1)print('eating end')
def play():print('playing start')gevent.sleep(1)print('playing end')
g1=gevent.spawn(eat) # 开启一个协程
g2=gevent.spawn(play)
g1.join() # 主线程等待g1结束
g2.join() # 主线程等待g2结束
运行结果:
协程对time.sleep(1)不会有效果。只对gevent.sleep(1)有效果。除非用monkey模块。如下:
from gevent import monkey;monkey.patch_all() # 有了这句代码,自动会把IO操作打包。否则就需要用gevent.sleep()
import gevent
def eat():print('eat:', threading.current_thread().getName()) #DummyThread-1假线程print('eating start')time.sleep(1) # IO事件print('eating end')
def play():print('play:', threading.current_thread().getName())print('playing start')time.sleep(1) # IO事件print('playing end')
g1=gevent.spawn(eat) # 开启一个协程
g2=gevent.spawn(play)
g1.join()
g2.join()
运行结果:
协程属于伪线程
6 同步和异步
# 进程和线程的状态切换是由操作系统完成 # 协程任务之间的切换是由程序(代码)完成。# 只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果#同步和异步:在高IO之间使用协程操作# 协程 : 能够在一个线程中实现并发效果的概念# 能够规避一些任务中的IO操作# 在任务的执行过程中检测到IO就切换到其他任务
from gevent import monkey;monkey.patch_all() # 必须写在import其他模块之前
import time
import gevent
def task():time.sleep(1)print(12345)
def sync():for i in range(10):task()
def async():g_lst=[]for i in range(10):g=gevent.spawn(task)g_lst.append(g)# for g in g_lst:g.join()gevent.joinall(g_lst)
sync()
async()
运行结果:
输出过程:先一秒一秒输出12345.输出四个后。同时输出5个12345
7 利用协程实现socket聊天
server模块:
import socket
from gevent import monkey;monkey.patch_all()
import gevent
def talk(conn):conn.send(b'hello')print(conn.recv(1024).decode('utf-8'))conn.close()
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
while True:conn,addr=sk.accept()gevent.spawn(talk,conn)
sk.close()
client模块:
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
print(sk.recv(1024))
msg=input('>>>').encode('utf-8')
sk.send(msg)
sk.close()
运行结果:
进程,线程,协程个数关系