• 第二十八篇、自定义线程池


    一、自定义线程池

    自定义线程池逻辑图

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import threading
    import queue
    """
    1、写了一个for循环搞300个任务,然后pool.run方法,之后给元祖里面添加任务,满足条件创建一个线程,
    紧接着第二个循环又进来了,第一个线程不一定能够执行完毕,然后满足条件再创建一个线程,
    接着第三次循环又见来了,这个时候如果前两个线程执行完毕之后,那么为了提高效率,也就不满足判断,就不会
    继续创建线程,而是用前两个线程取任务直接执行
    2、线程取的任务就是元祖,这里要思考个问题,就是怎么判断任务被取完成了(任务被封存到元祖里面了)
    元祖是任务,其他是终止符
    3、想要终止  --思路 查看还有多少线程,就增加多少个终止符,然后最后再执行close方法
    1)让正在从队列中取任务的线程挂掉
    2)主线程,你跳我就跳
    
    4、让当前的任务立即执行,即不管后面还有多少个任务,直接现在线程结束执行
    """
    
    StopEvent = object()   #这个是停止标志,但线程执行到队列中这里的时候说明这个停止标志是最后一个,也就是任务执行完毕了
    class ThreadPool():
        def __init__(self,max_num):
            self.q = queue.Queue()  #创建一个队列,没有指定参数,说明可以接受无限个东西
            self.max_num = max_num  #最多创建的线程数(线程池最大容量)
    
            self.generate_list = []  #真实创建的线程列表
            self.free_list = []    #空闲的线程数量
            self.terminal = False   #默认为false的时候不终止
    
        def run(self,func,args,callback=None):
            '''
            线程池执行一个任务,func要干什么,args参数
            '''
            w = (func,args,callback,)  #封装到元祖里面
            self.q.put(w)    #把任务封装到队列里面
    
            if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: #如果创建的线程等于0或者小于最大线程数
                self.generate_thread()
    
        def generate_thread(self):
            '''
            创建一个线程
            '''
            t = threading.Thread(target=self.call) #创建线程调用call方法
            t.start()
    
        def call(self):
            """
            循环去获取任务函数并执行任务函数
            """
            current_thread = threading.current_thread()#获取当前线程
            self.generate_list.append(current_thread) #把线程放入到这个列表中
    
            event = self.q.get() #获取任务并且执行
            while event !=StopEvent:  #如果不是停止标志那么就是任务
                #是元祖  ==》任务
                #解开任务包
                #执行任务
    
                #线程执行任务可能会出错
                func,args,callback = event  #event就是任务,任务也就是元祖,元祖里面有三个元素
                status =True
                try:
                    ret = func(args)  #执行func函数,循环里面执行func函数,参数传入就执行任务函数
                except Exception as e:
                    status = False #把标志发给回调函数
                    ret = e  #把错误信息发给回调函数
                if callback == None: #设置是否有毁掉函数,有了就传入,没有就不传
                    pass
                else:
                    callback(status,ret) #把上面函数结果传递给回调函数
    
                if self.terminal:   #如果为false就取任务
                    event = StopEvent
                else:
                     #如果任务全部执行了之后  应该标记:我空闲了
                    self.free_list.append(current_thread) #任务完成之后把线程添加到空闲任务里面
                    event = self.q.get()
                    self.free_list.remove(current_thread)  #如果取到任务,那么就把这个线程从空闲列表中移除
    
            else:
                #不是元祖,不是任务,这个时候就应该把执行停止标志的线程移除掉
                self.generate_list.remove(current_thread)
    
        def close(self):#添加终止符
            """
            思路:看结尾还有多少个线程就增加多少个终止符
            """
            num = len(self.generate_list)
            while num:
                self.q.put(StopEvent)
                num -=1
    
    
        def terminate(self): #终止线程,(不清空队列)
            self.terminal=True
            while self.generate_list:
                self.q.put(StopEvent)
    
            self.q.queue.clear()
    
    
            # #清空队列,先清空队列然后终止线程
            # self.q.empty()
            # #有可能出现等待的线程,所以要把全部线程终止
            # max_num = len(self.genarate_list)
            # while max_num:
            #     self.q.put(StopEvent)
            #     max_num -=1
    
    
    
    import time
    def action(i):
        time.sleep(0.5)
        print(i)
    
    
    pool = ThreadPool(10)
    for item in range(50):#这里的300个就是任务,将任务放置在队列中
        """
        开始处理任务
          - 创建线程
                - 有空闲线程则不再创建线程
                - 如果没有空闲线程,就创建线程 ,不能高于线程池的限制
                - 根据任务个数判断,如果任务个数低于线程池数,那么就创建任务个数线程,如果高于那么就创建线程池数个线程
          - 线程去队列中取任务
        """
        pool.run(func=action,args=(item,))
    time.sleep(0.1)
    pool.terminate()
    # time.sleep
    # print(len(pool.generate_list))
    # pool.close()(2)
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import queue
    import threading
    import contextlib
    import time
    
    StopEvent = object()
    
    
    class ThreadPool(object):
    
        def __init__(self, max_num, max_task_num = None):
            if max_task_num:
                self.q = queue.Queue(max_task_num)
            else:
                self.q = queue.Queue()
            self.max_num = max_num
            self.cancel = False
            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
            """
            if self.cancel:
                return
            if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
                self.generate_thread()
            w = (func, args, callback,)
            self.q.put(w)
    
        def generate_thread(self):
            """
            创建一个线程
            """
            t = threading.Thread(target=self.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 = func(*arguments)
                    success = True
                except Exception as e:
                    success = False
                    result = None
    
                if callback is not None:
                    try:
                        callback(success, result)
                    except Exception as e:
                        pass
    
                with self.worker_state(self.free_list, current_thread):
                    if self.terminal:
                        event = StopEvent
                    else:
                        event = self.q.get()
            else:
    
                self.generate_list.remove(current_thread)
    
        def close(self):
            """
            执行完所有的任务后,所有线程停止
            """
            self.cancel = True
            full_size = len(self.generate_list)
            while full_size:
                self.q.put(StopEvent)
                full_size -= 1
    
        def terminate(self):
            """
            无论是否还有任务,终止线程
            """
            self.terminal = True
    
            while self.generate_list:
                self.q.put(StopEvent)
    
            self.q.queue.clear()
    
        @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()
    优化后的代码

    二、上下文管理之with自定义open

     要用自定义上下文管理必须要用到下面的装饰器

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import contextlib
    import queue
    @contextlib.contextmanager
    def worker_state(xxx,val):
        xxx.append(val)
        try:
            yield 123
        finally:
            xxx.remove(val)
    
    q = queue.Queue()
    q.put("aa")
    li = []
    with worker_state(li,2) as f:
        print(f)
        q.get()
    View Code
    1 """
    2 上面执行流程
    3 1、先创建队列q
    4 2、往队列里面放入值
    5 3、往下执行with方法,执行worker_state(li,1): 函数
    6 4、执行xxx.append(val),往xxx里面放入值
    7 5、执行yield方法,然后执行get方法,从队列里面取值
    8 6、最后执行remove方法
    9 """
    流程说明

    执行顺序

     扩展:

    包含yield 关键字的函数成为一个迭代器,yield跟return的位置一样,只不过每次返回结果后,并没有退出,而是等待下一次迭代,下一次迭代开始后从yield后的语句开始执行,直到再次遇到yield,返回新一次的结果。

  • 相关阅读:
    BZOJ 1968: [Ahoi2005]COMMON 约数研究
    BZOJ 2463: [中山市选2009]谁能赢呢?
    BZOJ 2462: [BeiJing2011]矩阵模板
    hihoCoder 后缀自动机三·重复旋律6
    hihoCoder #1445 : 后缀自动机二·重复旋律5
    BZOJ 2179: FFT快速傅立叶
    BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡
    BZOJ 2946: [Poi2000]公共串
    BZOJ 2882: 工艺
    SPOJ LCS2
  • 原文地址:https://www.cnblogs.com/pyrene/p/6498180.html
Copyright © 2020-2023  润新知