zoukankan      html  css  js  c++  java
  • python基础-第九篇-9.3线程池

    简单版

      

    import queue
    import threading
    
    
    class ThreadPool(object):
    
        def __init__(self, max_num=20):
            self.queue = queue.Queue(max_num)
            for i in range(max_num):
                self.queue.put(threading.Thread)
    
        def get_thread(self):
            return self.queue.get()
    
        def add_thread(self):
            self.queue.put(threading.Thread)
    
    #实例化线程池对象
    pool = ThreadPool(10)
    #执行构造方法--实例一个max_num=10的队列,并往队列上添加10个线程类
    
    
    def func(arg, p):
        print(arg)
        import time
        time.sleep(2)
        #在线程执行完前往队列里再加上一个线程类
        p.add_thread()
    
    #生成30个任务
    for i in range(30):
        #每生成一个任务就从队列里取出一个线程类
        thread = pool.get_thread()
        #并实例化线程对象来执行任务
        t = thread(target=func, args=(i, pool))
        #线程启动后,执行func函数
        t.start()
    

       我们可以看到,简单版的线程池就是简单的,逻辑就是每次执行一个任务就从队列取一个线程类创建一个线程,所以就有了--多少个任务,多少个线程,那和进程池相比较下,你会从中发现哪些不足呢?

    • 第一,首先问你,执行完的线程去哪呢?--被程序回收销毁了!   那么执行完这些任务有必要一对一的创建线程吗??--如果任务执行的快,一个线程有时候可以做多个任务
    • 第二,上面只规定了队列的长度,并没有规定线程池里的线程数量??
    • 第三,进程池有回调函数这么一说法,上面没有?
    • 第四,进程池里提供了close和terminate方法...

     

    绝版

      

      好!我们来看看代码,是怎样解决上面不足的:

    import queue
    import threading
    import contextlib
    import time
    
    #放入队列里,做为线程停止运行的信号
    StopEvent = object()
    
    
    class ThreadPool(object):
    
        def __init__(self, max_num, max_task_num = None):
            #对max_task_num进行判断
            if max_task_num:
                #如果有值传入,在创建队列时,就按传入值限定队列长度
                self.q = queue.Queue(max_task_num)
            else:
                #否则,就默认为队列长度为无限长
                self.q = queue.Queue()
            #最大线程数
            self.max_num = max_num
            #close方法的状态码
            self.cancel = False
            #terminate方法的状态码
            self.terminal = False
            #生成线程列表
            self.generate_list = []
            #空闲线程列表
            self.free_list = []
    
        def run(self, func, args, callback=None):
            """
            线程池执行一个任务
            :param func: 任务函数
            :param args: 任务函数所需参数
            :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
            :return: 如果线程池已经终止,则返回True否则None
            """
    
            #如果执行close方法,这里就会执行return--中止函数,等同不再创建线程
            if self.cancel:
                return
            #如果空闲线程列表里为空并且已生成的线程数没有超过规定的最大线程数
            if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
                #那么就调用generate_thread方法创建线程
                self.generate_thread()
            #打包任务
            w = (func, args, callback,)
            #把打包的任务放入到队列里
            self.q.put(w)
    
        def generate_thread(self):
    
            #创建了一个线程
            t = threading.Thread(target=self.call)
            #启动线程,执行call方法
            t.start()
    
        def call(self):
            """
            循环去获取任务函数并执行任务函数
            """
    
            #获取当前线程变量
            current_thread = threading.currentThread
            #把当前线程添加到生成线程列表里
            self.generate_list.append(current_thread)
    
            #到队列里去拿任务
            event = self.q.get()
            #对取到的任务进行判断
            while event != StopEvent:
                #如果任务不等于停止信号,进入循环
    
                #解任务包
                func, arguments, callback = event
                #尝试执行任务包里的函数
                try:
                    #执行成功,拿到result执行结果
                    result = func(*arguments)
                    #并给执行状态赋值True
                    success = True
                except Exception as e:
                    #执行失败,赋值执行状态False
                    success = False
                    #同时赋值执行结果为None
                    result = None
    
                if callback is not None:
                    #如果callback不是默认None,就尝试下列操作
                    try:
                        #把执行状态和执行结果传给回调函数,执行
                        callback(success, result)
                    except Exception as e:
                        pass
    
                #with上下文管理--等同把free_list,current_thread传给worker_state方法,并执行
                with self.worker_state(self.free_list, current_thread):
                    #判断self.terminal的状态
                    if self.terminal:
                        #如果状态为True,就把任务变量设置为停止信号,等同于中止当前线程
                        event = StopEvent
                    else:
                        #否则,就去队列里取任务
                        event = self.q.get()
            else:
                #如果是停止信号,就把当前线程从生成线程列表中移除
                self.generate_list.remove(current_thread)
    
        def close(self):
            """
            执行完所有的任务后,所有线程停止
            """
    
            #当执行close方法时,就把cancel状态设置为True
            self.cancel = True
            full_size = len(self.generate_list)
            while full_size:
                #往队列里加停止信号,直到生成线程列表长度为0
                self.q.put(StopEvent)
                full_size -= 1
    
        def terminate(self):
            """
            无论是否还有任务,终止线程
            """
    
            #当执行terminate方法时,把terminal状态设置为True
            self.terminal = True
            #因为突然中止,难免队列里还有任务,所以先清空一下队列
            self.q.empty()
            while self.generate_list:
                #往队列里加停止信号,值到生成线程列表为空时
                self.q.put(StopEvent)
    
    
    
        @contextlib.contextmanager
        def worker_state(self, state_list, worker_thread):
            """
            用于记录线程中正在等待的线程数
            """
    
            #把执行完任务的当前线程添加到空闲线程列表里
            state_list.append(worker_thread)
            try:
                #暂时跳出
                yield
            finally:
                #把当前线程从空闲线程列表里移除
                state_list.remove(worker_thread)
    
    
    
    # How to use
    
    
    pool = ThreadPool(5)
    
    def callback(status, result):
        # status, execute action status
        # result, execute action return value
        pass
    
    
    def action(i):
        print(i)
    
    for i in range(30):
        ret = pool.run(action, (i,), callback)
    
    time.sleep(5)
    print(len(pool.generate_list), len(pool.free_list))
    print(len(pool.generate_list), len(pool.free_list))
    # pool.close()
    # pool.terminate()
    

               欢迎大家对我的博客内容提出质疑和提问!谢谢

                                                                                 笔者:拍省先生

  • 相关阅读:
    #检查磁盘使用率超过90%,并且后台进程没有rman在跑,就运行 /data/script/del_dg_arch.sh 脚本清理归档
    linux shell数据重定向
    创建用户
    Linux HA+ Oracle 安装维护手册
    解决UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range
    Linux 文件不能被root修改与编辑原因
    python中的时间戳和格式化之间的转换
    Python-Redis-发布订阅
    Python-Redis-常用操作&管道
    Python-Redis-Set
  • 原文地址:https://www.cnblogs.com/xinsiwei18/p/5709215.html
Copyright © 2011-2022 走看看