import queue
import threading
import contextlib
import time

"""
一个基于thread和queue的线程池,以任务为队列元素,动态创建线程,重复利用线程,通过close和terminate方法关闭线程池。
"""
# 创建空对象,用于停止线程
StopEvent = object()


class ThreadPool(object):

    def __init__(self, max_num, max_task_num=None):
        """
        max_num ==> 线程池最大线程数
        max_task_num ==> 任务队列长度
        """
        # 如果设置了最大任务参数,则将队列的最大元素个数设置为该值。
        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
        # 将任务函数、参数、回调函数打包放入一个元组中
        w = (func, args, callback,)
        # 将任务放入队列
        self.q.put(w)

        if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
            self.generate_thread()  # 创建一个线程

    def generate_thread(self):
        """
        创建一个线程
        """
        t = threading.Thread(target=self.call)  # 在线程中执行call函数
        t.start()

    def call(self):
        """
        循环去获取任务函数并执行任务函数,正常下,每个线程都保存生存状态,直到获取线程终止的flag
        """
        # 获取当前线程的名字
        current_thread = threading.currentThread().getName()
        # 将当前线程的名字加入已实例化的线程列表中
        self.generate_list.append(current_thread)
        # 在任务队列中获取一个任务
        event = self.q.get()
        # 获取的任务不是终止线程的标识对象时
        while event != StopEvent:
            # 解析任务中封装的三个参数
            func, args, callable = event
            # 异常判断
            try:
                ret = func(current_thread, *args)
                status = True
            except Exception as e:
                status = False
                ret = e
            if callable != None:
                try:
                    callable(status, ret)
                except Exception as e:
                    pass
            # 当某个线程正常执行完一个任务时,执行work_state方法
            with work_state(self.free_list, current_thread):
                # 如果是强制关闭线程
                if self.terminal:  # False
                    event = StopEvent
                # 获取一个正常的任务,并回调work_state方法中的yield语句
                else:
                    # 在这里又开始一个正常的任务循环
                    event = self.q.get()
        else:
            # 发现任务是一个终止线程的标识,将线程在已创建线程列表中删除
            self.generate_list.remove(current_thread)

    def close(self):
        """
        执行完所有任务后,让所有线程都停止
        """
        # 设置flag
        self.cancel = True
        # 计算已创建线程列表中线程的数,然后向任务队列中推送相同数量的终止线程标识
        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.empty()

# 该装饰器用于上下文管理


@contextlib.contextmanager
def work_state(free_list, thread):
    # 将当前线程,添加到空闲线程列表中
    free_list.append(thread)
    try:
        # 等待
        yield
    finally:
        # 将线程移除空闲列表
        free_list.remove(thread)


def callable(status, result):
    """
    根据需要进行回调函数,默认不执行
    status ==> 函数的执行状态
    result ==> 函数返回值
    """
    pass


def action(thread_name, i):
    """
    真实任务定义在该函数中
    thread_name ==> 执行该方法的线程名
    i ==> 该函数需要的参数
    """
    time.sleep(0.5)
    print(thread_name, i)

# 创建一个最多包含10个线程的线程池
pool = ThreadPool(10)
# 创建50个任务
for i in range(50):
    pool.run(action, (i,), callable)

# time.sleep(3)
print("33[32;0m任务停止前线程池中有%s个线程,空闲的线程有%s个。33[0m" %(len(pool.generate_list), len(pool.free_list)))
# 正常关闭线程池
pool.close()

# 强制关闭线程
# pool.terminate()

 

posted on 2016-09-18 15:02  雷雷嘎嘎  阅读(69)  评论(0)    收藏  举报