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

一、自定义线程池

自定义线程池逻辑图

#!/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,返回新一次的结果。

posted @ 2017-03-03 19:30  pi-pi-miao-miao  阅读(188)  评论(0编辑  收藏  举报