1、基础
# 进程 启动多个进程 进程之间是由操作系统(时间片轮转)负责调用 # 线程 启动多个线程 真正被CPU执行的最小单位是线程 # 开启一个线程 创建一个线程 寄存器 堆栈 # 关闭一个线程 等都需要时间 # 协程:本质上是一个线程 # 能够在多个任务之间切换来节省一些IO时间 # 协程切换也需要时间,但是远远小于线程进程开销时间 # 实现并发的手段
2 协程应用背景
import time
'''1'''
def consumer(): # 生成器函数
print(123)
while True:
x=yield
print('**',x)
c=consumer() # c为生成器,此时函数并没有执行
next(c)
c.send(1)
程序执行顺序:
程序运行结果:
3 在一个程序上进行两个任务的切换
'''2 在一个程序上进行两个任务的切换'''
def consumer(): # 生成器函数
while True:
x=yield
time.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 gevent
def 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()
运行结果:
进程,线程,协程个数关系