协程:是单线程下的并发,又称微线程。
什么是线程?:
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
协程的本质
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率
对于不涉及io的操作,单纯的切换反而会降低效率
#并发执行
import time
def producer():
g = consumer()
next(g)
for i in range(10000000):
g.send(i)
def consumer():
while True:
res = yield
start_time = time.time()
producer()
stop_time = time.time()
print(stop_time-start_time)
#串行
import time
def producer():
res=[]
for i in range(10000000):
res.append(i)
return res
def consumer(res):
pass
start_time=time.time()
res=producer()
consumer(res)
stop_time=time.time()
print(stop_time-start_time)
总结协程的优缺点:
优点:
1、协程开销小,属于程序级别的切换,操作系统感知不到
2、单线程下可以实现并发效果, 最大限度地利用CPU
缺点
1、协程的本质是单线程下,无法利用多核优势。
2、协程是指单线程,一旦协程出现阻塞,将会阻塞整个线程。
协程的特点:
1、必须在一个单线程里实现并发
2、修改共享数据不加锁
3、用户程序自己控制保存上下文
4、一个协程遇到io操作自动切换到其他协程。
grennlet模块
并不能监听(不能遇到io自动切换)
from greenlet import greenlet
def func1():
print('123')
g2.switch()
print('456')
def func2():
print('abc')
g1.switch()
print('qqq')
g1.switch()
if __name__ == '__main__':
g1 = greenlet(func1)
g2 = greenlet(func2)
g1.switch()
gevent模块
#用法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的
g2=gevent.spawn(func2)
g1.join() #等待g1结束
g2.join() #等待g2结束
#或者上述两步合作一步:gevent.joinall([g1,g2])
g1.value#拿到func1的返回值
遇到io自动切换
import gevent
def eat(name):
print('%s eat 1' %name)
gevent.sleep(2)
print('%s eat 2' %name)
def play(name):
print('%s play 1' %name)
gevent.sleep(1)
print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')
同步与异步的性能比较
import gevent
import time
def task(i):
gevent.sleep(1)
print(i)
# res = 0
# for i in range(1000):
# res = i**i
# return res
def async():
threads = [gevent.spawn(task, i) for i in range(10)]
gevent.joinall(threads)
def sync():
for i in range(10):
gevent.spawn(task, i)
t1 = time.time()
sync()
t2 = time.time()
async()
t3 = time.time()
print('sync run time', t2 - t1)
print('asyn run time', t3 - t2)
协程实现FTP
服务端
from gevent import monkey;monkey.patch_all()
import socket
import gevent
def read(conn):
try:
while True:
data = conn.recv(1024).decode()
if not data:
break
conn.send(data.upper().encode())
except Exception as e:
print(e)
conn.close()
if __name__ == '__main__':
server = socket.socket()
server.bind(('127.0.0.1', 9999))
server.listen(10)
while True:
conn, addr = server.accept()
gevent.spawn(read, conn)
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 9999))
while True:
msg = input('>>').strip()
if not msg: continue
client.send(msg.encode())
data = client.recv(1024).decode()
print(data)
协程实现生产者消费者模型
import queue
import gevent
def comsumer():
print("消费者")
while True:
gevent.sleep(0.1)
res = q.get()
# print('res', res)
if not res:
break
print('消耗了', res)
def producer(n):
print("生产者")
for i in range(1, 10):
q.put(i)
print('%s生产了%s' % (n, i))
if __name__ == '__main__':
q = queue.Queue()
p1 = gevent.spawn(producer, 'p1')
p2 = gevent.spawn(producer, 'p2')
p3 = gevent.spawn(producer, 'p3')
c1 = gevent.spawn(comsumer)
p1.join()
p2.join()
p3.join()
q.put(0)
c1.join()
print('zhu')