• Python的并发并行[2] -> 队列[1] -> 使用队列进行任务控制


    使用队列进行任务控制


    1 FIFOLIFO队列

    FIFO(First In First Out)与LIFO(Last In First Out)分别是两种队列形式,在FIFO中,满足先入先出的队列方式,而LIFO则是后入先出的队列形式,利用这两种方式可以实现不同的队列功能。

     1 from random import randint
     2 from time import sleep, ctime
     3 from queue import Queue, LifoQueue
     4 from threading import Thread
     5 
     6 COUNT = 0
     7 
     8 
     9 class MyThread(Thread):
    10     """
    11     Bulid up a Module to make this subclass more general
    12     And get return value by add a function named 'getResult()'
    13     """
    14     def __init__(self, func, args, name=''):
    15         Thread.__init__(self)
    16         self.name = name
    17         self.func = func
    18         self.args = args
    19 
    20     def getResult(self):
    21         return self.res
    22 
    23     def run(self):
    24         print('Starting', self.name, 'at:', ctime())
    25         # Call function here and calculate the running time
    26         self.res = self.func(*self.args)
    27         print(self.name, 'finished at:', ctime())
    28 
    29 
    30 class MyQueue():
    31     def __init__(self):
    32         self.funcs = [self.writer, self.reader]
    33         self.nfuncs = range(len(self.funcs))
    34 
    35     def writeQ(self, queue):
    36         global COUNT
    37         print('Producing object OBJ_%d for Q...' % COUNT, end=' ')
    38         queue.put('OBJ_%d' % COUNT, True)
    39         print('size now:', queue.qsize())
    40         COUNT += 1
    41     
    42     def readQ(self, queue):
    43         # If queue is empty, block here until queue available
    44         val = queue.get(True)
    45         print('Consumed object %s from Q... size now:' % val, queue.qsize())
    46     
    47     def writer(self, queue, loops):
    48         for i in range(loops):
    49             self.writeQ(queue)
    50             sleep(randint(1, 3))
    51     
    52     def reader(self, queue, loops):
    53         for i in range(loops):
    54             self.readQ(queue)
    55             sleep(randint(2, 5))
    56     
    57     def main(self):
    58         nloops = randint(2, 5)
    59         fifoQ = Queue(32)
    60         lifoQ = LifoQueue(32)
    61     
    62         # First In First Out mode for Queue
    63         print('-----Start FIFO Queue-----')
    64         threads = []
    65         for i in self.nfuncs:
    66             threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
    67         for t in threads:
    68             t.start()
    69         for t in threads:
    70             t.join()
    71         # Last In First Out mode for LifoQueue
    72         print('-----Start LIFO Queue-----')
    73         threads = []
    74         for i in self.nfuncs:
    75             threads.append(MyThread(self.funcs[i], (lifoQ, nloops), self.funcs[i].__name__))
    76         for t in threads:
    77             t.start()
    78         for t in threads:
    79             t.join()
    80     
    81         print('All DONE')
    82 
    83 if __name__ == '__main__':
    84      MyQueue().main()

    第 1-27 行,首先对需要的模块进行导入,并定义一个全局变量的计数器,派生一个MyThread线程类,用于调用函数及其返回值(本例中MyThread可用于接受writer和reader函数,同时将Queue的实例作为参数传给这两个函数)。

    第 30-79 行,定义一个队列类,用于进行队列一系列处理,其中writeQ与readQ会分别对队列执行put和get函数,在writeQ中利用全局变量设置每个加入队列的对象的名字。而writer和reader则会利用循环多次执行writeQ和readQ函数。最后定义一个main函数,用于生成队列,同时调用FIFO以及LIFO两种队列方式。

    运行得到结果

    -----Start FIFO Queue-----  
    Starting writer at: Tue Aug  1 21:43:22 2017  
    Producing object OBJ_0 for Q... size now: 1  
    Starting reader at: Tue Aug  1 21:43:22 2017  
    Consumed object OBJ_0 from Q... size now: 0  
    Producing object OBJ_1 for Q... size now: 1  
    Producing object OBJ_2 for Q... size now: 2  
    Producing object OBJ_3 for Q... size now: 3  
    Consumed object OBJ_1 from Q... size now: 2  
    writer finished at: Tue Aug  1 21:43:26 2017  
    Consumed object OBJ_2 from Q... size now: 1  
    Consumed object OBJ_3 from Q... size now: 0  
    reader finished at: Tue Aug  1 21:43:34 2017  
    -----Start LIFO Queue-----  
    Starting writer at: Tue Aug  1 21:43:34 2017  
    Producing object OBJ_4 for Q... size now: 1  
    Starting reader at: Tue Aug  1 21:43:34 2017  
    Consumed object OBJ_4 from Q... size now: 0  
    Producing object OBJ_5 for Q... size now: 1  
    Producing object OBJ_6 for Q... size now: 2  
    Producing object OBJ_7 for Q... size now: 3  
    writer finished at: Tue Aug  1 21:43:38 2017  
    Consumed object OBJ_7 from Q... size now: 2  
    Consumed object OBJ_6 from Q... size now: 1  
    Consumed object OBJ_5 from Q... size now: 0  
    reader finished at: Tue Aug  1 21:43:53 2017  
    All DONE  
    View Code

    从输出可以看出,FIFO满足先入先出,LIFO满足后入先出的队列形式。

    2 join挂起与task_done信号

    在queue模块中,Queue类提供了两个用于跟踪监测任务完成的函数,join和task_done,对于join函数来说,当Queue的类实例调用了join函数挂起时,join函数会阻塞等待,一直到join之前进入队列的所有任务全部标记为task_done后才会解除阻塞。

    Note: 通过查看Queue的源码可以看出,在调用put函数时,会对类变量unfinished_tasks进行数值加1,而调用get函数时并不会将unfinished_tasks进行减1,只有调用task_done函数才会导致变量减1。而调用join函数时,join函数会对这个unfinished_tasks变量进行获取,也就是说,join函数会获取到在调用之前所有被put进队列里的任务中,还没有调用过task_done函数的任务数量,无论这个任务是否已经被get出列。

    下面的例子中,以Queue_FIFO_LIFO.py中的MyQueue为基类,派生出一个新类,用于测试join函数与task_done函数。

     1 from Queue_FIFO_LIFO import *
     2 
     3 class NewQueue(MyQueue):
     4     def __init__(self):
     5         MyQueue.__init__(self)
     6 
     7     def writer(self, queue, loops):
     8         for i in range(loops):
     9             self.writeQ(queue)
    10             sleep(randint(1, 3))
    11         print('Producing join here, waiting consumer')
    12         queue.join()
    13     
    14     def reader(self, queue, loops):
    15         for i in range(loops):
    16             self.readQ(queue)
    17             sleep(randint(2, 5))
    18             print('OBJ_%d task done' % i)
    19             queue.task_done()
    20 
    21     def main(self):
    22         nloops = randint(2, 5)
    23         fifoQ = Queue(32)
    24 
    25         print('-----Start FIFO Queue-----')
    26         threads = []
    27         for i in self.nfuncs:
    28             threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
    29         for t in threads:
    30             t.start()
    31         for t in threads:
    32             t.join()
    33 
    34         print('All DONE')
    35 
    36 if __name__ == '__main__':
    37     NewQueue().main()

    上面的代码,在导入模块后,调用MyQueue的初始化函数进行初始化设置。在新类NewQueue中,对原基类的writer和reader以及main方法进行了重载,加入了join函数和task_done函数,并在main函数中只采用FIFO队列进行试验。

    运行得到结果

    -----Start FIFO Queue-----  
    Starting writer at: Wed Aug  2 09:06:40 2017  
    Producing object OBJ_0 for Q... size now: 1  
    Starting reader at: Wed Aug  2 09:06:40 2017  
    Consumed object OBJ_0 from Q... size now: 0  
    Producing object OBJ_1 for Q... size now: 1  
    Producing object OBJ_2 for Q... size now: 2  
    OBJ_0 task done  
    Consumed object OBJ_1 from Q... size now: 1  
    Producing object OBJ_3 for Q... size now: 2  
    Producing object OBJ_4 for Q... size now: 3  
    Producing join here, waiting consumer  
    OBJ_1 task done  
    Consumed object OBJ_2 from Q... size now: 2  
    OBJ_2 task done  
    Consumed object OBJ_3 from Q... size now: 1  
    OBJ_3 task done  
    Consumed object OBJ_4 from Q... size now: 0  
    OBJ_4 task done  
    reader finished at: Wed Aug  2 09:07:02 2017  
    writer finished at: Wed Aug  2 09:07:02 2017  
    All DONE  
    View Code

    通过得到的结果可以看出,当新类里的writer完成了自己的Producing任务后,会由join挂起,一直等待直到reader的Consuming全部完成且标记task_done之后,才会解除挂起,此时writer和reader将会一起结束退出。

    相关阅读


    1. 多线程的建立

    2. queue 模块

    参考链接 


     

    《Python 核心编程 第3版》

  • 相关阅读:
    基于redis的分布式锁实现方案
    nginx 配置文件详解
    Linux 下安装Nginx
    Nginx简介
    Spring Boot 整合 Spring Security 示例实现前后分离权限注解 + JWT 登录认证
    idea2020.1版本下载安装与激活
    Cloud Alibaba --Nacos
    spring Cloud Alibaba 简介
    Eclipse中SVN更改连接用户
    Win7(64bit)搭建SVN
  • 原文地址:https://www.cnblogs.com/stacklike/p/8166854.html
Copyright © 2020-2023  润新知