多任务、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

 

posted @ 2019-01-08 12:42  greenfan  阅读(301)  评论(0)    收藏  举报