python threadpool线程池简单实现,了解原理

简单实现了一下threadpool,避免了只会调用却不知道如何运行的尴尬,详细代码如下:

import queue
import threading
import contextlib
import time

"""
author:LancerWu
email: wuxs231@163.com
description:
一个基于thread和queue的线程池,以任务为队列元素,动态创建线程,重复利用线程,通过class和terminate的方法关闭线程池
"""

# 创建空对象,用于停止线程
stop_event = object()


def callback(status, result):
    '''
    根据需要进行的回调函数,默认不执行
    :param status: 函数的执行状态
    :param result: 函数的返回值
    :return:
    '''
    pass


def action(thread_name, arg):
    '''
    真实的任务定义在这个函数里
    :param thread_name: 执行该方法的线程名
    :param arg: 该函数需要的参数
    :return:
    '''
    # 模拟该函数执行了0.1秒
    time.sleep(0.1)
    print("第%s个任务调用了线程%s" % (arg+1, thread_name))


class ThreadPool:
    def __init__(self, max_num, max_task_num=None):
        '''
        初始化线程池
        :param max_num:线程池最大线程数量
        :param 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 put(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):
        '''
        创建一个线程
        :return:
        '''
        # 每个线程都执行call方法
        t = threading.Thread(target=self.call)
        t.start()

    def call(self):
        '''
        循环去获取任务并执行任务函数,在正常情况下,每个线程都保存生存状态,直到获取到线程终止的flag。
        :return:
        '''
        # 获取当前线程的名字
        current_thread = threading.currentThread().getName()

        # 将当前线程的名字加入到已经实例化的线程列表中
        self.generate_list.append(current_thread)

        # 从任务队列中获取一个任务
        event = self.q.get()

        # 当获取的任务不是终止线程的标识对象时
        while event != stop_event:
            # 解析任务重封装的三个参数
            func, arguments, callback = event

            # 抓取异常,防止线程因为异常而退出
            try:
                # 正常执行任务函数
                result = func(current_thread, *arguments)
                success = True

            except Exception as e:
                # 当任务执行过程中弹出异常
                result = None
                success = False

            # 如果有指定的回调函数
            if callback is not None:
                # 执行回调函数,并抓取异常
                try:
                    callback(success, result)

                except Exception as e:
                    pass

                # 当某个线程正常执行完一个任务时,先执行worker_state方法
                with self.worker_state(self.free_list, current_thread):
                    # 如果强制关闭线程的flag开启,则传入一个stop_event元素
                    if self.terminal:
                        event = stop_event
                    # 否则获取一个正常的任务,并回调worker_state方法的yield语句
                    else:
                        # 从这里开始又是一个正常的任务循环
                        event = self.q.get()
            else:
                # 一旦发现任务是个终止线程的标识元素,将线程从已创建线程列表中删除
                self.generate_list.remove(current_thread)

    def close(self):
        '''
        执行完所有任务之后,让所有线程都停止的方法
        :return:
        '''
        # 设置flag
        self.cancel = True

        # 计算已创建线程列表中的线程个数,然后往任务队列里推送相同数量的终止线程的标识元素
        full_size = len(self.generate_list)
        while full_size:
            self.q.put(stop_event)
            full_size -= 1

    def terminate(self):
        '''
        在任务执行过程中,终止线程,提前退出
        :return:
        '''
        self.terminal = True

        # 强制性的停止线程
        while self.generate_list:
            self.q.put(stop_event)

    # 该装饰器用于上下文管理
    @contextlib.contextmanager
    def worker_state(self, state_list, worker_thread):
        '''
        用于记录空闲的线程,或者从空闲的线程中取出线程处理任务
        :param state_list:
        :param worker_thread:
        :return:
        '''
        # 将当前线程,添加到空闲线程列表中
        state_list.append(worker_thread)

        # 捕获异常
        try:
            # 在此等待
            yield

        except Exception as e:
            print('worker_state error:%s' % e)

        finally:
            # 将线程从空闲列表中移除
            state_list.remove(worker_thread)


# 调用执行
if __name__=='__main__':
    # 创建一个最多包含5个线程的线程池
    pool = ThreadPool(5)

    # 创建100个任务,让线程池进行处理
    for i in range(100):
        pool.put(action, (i,), callback)

    # 等待一定时间,让线程执行完任务
    time.sleep(4)

    print('-' * 50)
    print('\033[32;0m 任务停止之前线程池中有%s个线程,空闲的线程有%s个! \033[0m' % (len(pool.generate_list), len(pool.free_list)))

    # 正常关闭线程池
    pool.close()
    print('任务执行完毕,正常退出')

    # 强制关闭线程池
    # pool.terminate()
    # print('已强制停止任务')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值