多任务、queue队列 greenlet gevent
多任务:协程占用资源最少,线程次之,进程最多。协程是并发执行多任务,同一时刻只有一个任务在执行,在多个任务中切换,是假的多任务。
协程依赖于线程,线程依赖于进程,进程挂线程就挂,线程挂协程就挂。
协程最基本的实现方法是yield,greenlet是重新封装了yield,gevent是重新封装了greenlet,所以要用协程就用gevent,yield和greenlet仅作为协程原理的理解。
import time
import threading
import multiprocessing
def a():
while True:
print("---1----")
time.sleep(0.1)
def b():
while True:
print("----2-----")
time.sleep(0.1)
# 进程池实现多任务
# po = multiprocessing.Pool(2)
# for i in range(100):
# po.apply_async(a)
# po.close()
# po.join()
# 多线程实现多任务
# t1 = threading.Thread(target=a)
# t2 = threading.Thread(target=b)
# t1.start()
# t2.start()
# 多进程实现多任务
# p1 = multiprocessing.Process(target=a)
# p2 = multiprocessing.Process(target=b)
# p1.start()
# p2.start()
#通过生成器进行多任务,也就是协程
def a():
while True:
print("---1----")
time.sleep(0.1)
yield
def b():
while True:
print("----2-----")
time.sleep(0.1)
yield
def main():
a1 = a()
b1 = b()
while True:
next(a1)
b1.__next__()
# next(b1)
main()
yield协程可以用一个更高级的greenlet实现,它封装了yield,greenlet是第三方包,需要安装【pip3 install greenlet】示例:
import time import threading import multiprocessing from greenlet import greenlet def a(): while True: print("---1----") gr2.switch() #经测试,这个语句放在while循环中就可以,可以在print上面,也可以在time.sleep下面。 time.sleep(0.1) def b(): while True: print("----2-----") gr1.switch() time.sleep(0.1) #协程 greenlet, gr1 = greenlet(a) gr2 = greenlet(b) gr1.switch()
实际中用协程的时候,基本不用greenlet,会用gevent,gevent是封装了greenlet,同样gevent也是第三方模块,需要安装【pip3 install gevent】
下面是gevent的示例:
import gevent
def a1(n):
for i in range(n):
print(gevent.getcurrent,i)
def a2(n):
for i in range(n):
print(i)
def a3():
for i in range(10):
print(i)
g1 = gevent.spawn(a1,10)
g2 = gevent.spawn(a2,10)
g3 = gevent.spawn(a3)
g1.join()
g2.join()
g3.join()
这个时候,三个gevent对象的执行是顺序的,gevent是遇到延时就切换任务,但它的延时不是time.sleep(),而是gevent.sleep(),看下面的例子:
import gevent
def a1(n):
for i in range(n):
print(gevent.getcurrent,i)
gevent.sleep(0.2)
def a2(n):
for i in range(n):
print(i)
gevent.sleep(0.2)
def a3():
for i in range(10):
print(i)
gevent.sleep(0.2)
g1 = gevent.spawn(a1,10)
g2 = gevent.spawn(a2,10)
g3 = gevent.spawn(a3)
g1.join()
g2.join()
g3.join()
这样g1执行完第一次打印,遇到一个gevent.sleep()延时,任务就会切换到g2,g2打印完第一条也遇到延时,就切换到g3,然后又是g1,一次轮询执行完毕,实现单任务并发。
那么如果用socket时,recv、accept、connect等方法产生堵塞时,相当于延时,那么想要让gevent遇到这种延时自动切换,就需要想gevent.sleep一样,用gevent.socket(),但这明显是不方便的。解决方法:
from gevent import monkey #先导入monkey
monkey.petch_all() #在代码中加入这一句,先执行这一句,再执行后面的gevent代码
通过上面的补丁,就可以使用time.sleep()啦,见示例:
import gevent import time from gevent import monkey
monkey.patch_all() def a1(n): for i in range(n): print(gevent.getcurrent,i) time.sleep(0.2) def a2(n): for i in range(n): print(i) time.sleep(0.2) def a3(): for i in range(10): print(i) time.sleep(0.2) g1 = gevent.spawn(a1,10) g2 = gevent.spawn(a2,10) g3 = gevent.spawn(a3) g1.join() g2.join() g3.join()
monkey.patch_all()在代码上方先执行,它会把代码中耗时(堵塞)的地方,比如time.sleep()换成gevent.sleep(),socket.recv()换成gevent.socket.recv()
另外:
三个join可以用一句代码:
# g1.join()
# g2.join()
# g3.join()
gevent.joinall([g1,g2,g3])
或者:
gevent.joinall([gevent.spawn(a1),gevent.spawn(a2),gevent.spawn(a3)])
说明:要用gevent,先导入monkey,然后再打上补丁,再定义函数,然后创建gevent对象添加到gevent.joinall的列表中。
queue,先进先出
创建queue队列:
q = multiprocessing.Queue(4)
进程间的队列创建有所不同:
q = multiprocessing.Manager().Queue(4)
Queue()中的参数是队列数量,可以为空。
写入队列:
q.put(xxx)
读取队列:
q.get()
q.full:True,False
q.empyt:True,False

浙公网安备 33010602011771号