协程
1.通常在Python中我们进行并发编程一般都是使用多线程或者多进程来实现的,对于计算型任务由于GIL的存在我们通常使用多进程来实现,而对与IO型任务我们可以通过线程调度来让线程在执行IO任务时让出GIL,从而实现表面上的并发。
2.其实对于IO型任务我们还有一种选择就是协程,协程是运行在单线程当中的“并发”,协程相比多线程一大优势就是省去了多线程之间的切换开销,获得了更大的运行效率。Python中的asyncio也是基于协程来进行实现的。在进入asyncio之前我们先来了解一下Python中怎么通过生成器进行协程来实现并发
下来我们完成一个简单的协程
import time
def A():
while True:
print("-----A-----")
yield
time.sleep(0.5)
def B(c):
while True:
print("-------B-----")
next(c)
time.sleep(0.5)
if __name__=="__main__":
a=A()
B(a)
这是我们用生成器写的协程能模拟出并发的效果,用一个主进程去运行函数,利用生成器yield很好的演示了并发的效果。处了用生成器外,还可以用一个库,实现协程的并发。这个库就是 greenlet 这个库是python的第三方库,需要安装。
下来看代码:
from greenlet import *
import time
def A():
while True:
print("------a-----")
gr2.switch()
time.sleep(0.5)
def B():
while True:
print("------b------")
gr1.switch()
time.sleep(0.5)
gr1=greenlet(A)
gr2=greenlet(B)
gr1.switch()
这个模块开始创建了两个对象gr1和gr2,先让gr1运行A函数里面的代码打印出a线程跳转运行b函数打印出b线程跳转,以此来实现并发的效果。除了这些还有一个并发网络库 gevent 下来我给大家做个示范。
import sys
import gevent
import time
from gevent import socket,monkey
monkey.patch_all()
def handle_request(conn):
while True:
data=conn.recv(1024)
if not data:
conn.close():
break
print("recv",data)
conn.send(data)
def server(port):
s=socket.socket()
s.bind(("",port))
s.listen(5)
while True:
cli,addr=s.accept()
gevent.spawn(handle_request,cli)
if __name__=="__main__":
server(7788)
生产者消费者
import threading
import time
from queue import Queue
class Producer(threading.Thread):
def run(self):
global queue
count = 0
while True:
if queue.qsize() < 1000:
for i in range(100):
count = count +1
msg = '生成产品'+str(count)
queue.put(msg)
print(msg)
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global queue
while True:
if queue.qsize() > 100:
for i in range(3):
msg = self.name + '消费了 '+queue.get()
print(msg)
time.sleep(1)
if __name__ == '__main__':
queue = Queue()
for i in range(500):
queue.put('初始产品'+str(i))
for i in range(2):
p = Producer()
p.start()
for i in range(5):
c = Consumer()
c.start()
我用类写继承了threading.Thread,重写了父类里面的run方法,这样可以以解决耦合的问题,使代码更加灵活。