• 进程池和线程池


    为什么要用线程池

    系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

    线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。

    此外,使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致 Python 解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。可以有效的保护计算机的安全性。

    函数解读

    线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池

    Exectuor 提供了如下常用方法:

    • submit(fn, args, **kwargs):将 fn 函数提交给线程池。args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
    • map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
    • shutdown(wait=True):关闭线程池。

    程序将 task 函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。

    Future 提供了如下方法:

    • cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。

    • cancelled():返回 Future 代表的线程任务是否被成功取消。

    • running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。

    • done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。

    • result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。

    • exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。

    • add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。

    • 在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。

      使用线程池来执行线程任务的步骤如下:

      1. 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
      2. 定义一个普通函数作为线程任务。
      3. 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
      4. 当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线程池。

    怎么用线程池

    
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import time
    import os
    
    
    # 示例化池对象
    # 不知道参数的情况,默认是当前计算机cpu个数乘以5,也可以指定线程个数
    pool = ProcessPoolExecutor(5)  # 创建了一个池子,池子里面有20个线程
    
    def task(n):
        print(n,os.getpid())
        time.sleep(2)
        return n**2
    
    def call_back(n):
        print('我拿到了结果:%s'%n.result())
        
    """
    提交任务的方式
        同步:提交任务之后,原地等待任务的返回结果,再继续执行下一步代码
        异步:提交任务之后,不等待任务的返回结果(通过回调函数拿到返回结果并处理),直接执行下一步操作
    """
    
    
    # 回调函数:异步提交之后一旦任务有返回结果,自动交给另外一个去执行
    if __name__ == '__main__':
        # pool.submit(task,1)
        t_list = []
        for i in range(20):
            future = pool.submit(task,i).add_done_callback(call_back)  # 可通过 Future 的 add_done_callback() 方法来添加回调函数,该回调函数形如 fn(future)。当线程任务完成后,程序会自动触发该回调函数,并将对应的 Future 对象作为参数传给该回调函数。
            t_list.append(future)
    
        # pool.shutdown()  # 关闭池子并且等待池子中所有的任务运行完毕
        # for p in t_list:
        #     print('>>>:',p.result())
        print('主')
    
    
    

  • 相关阅读:
    [WC2011]最大XOR和路径 线性基
    线段树分裂合并
    [NOIp2016]天天爱跑步 线段树合并
    CF1111E Tree 树链剖分,DP
    [NOI2016]区间 线段树
    [IOI2018] werewolf 狼人 kruskal重构树,主席树
    [CQOI2012]组装 贪心
    [ONTAK2010]Peaks kruskal重构树,主席树
    [NOI2018]归程 kruskal重构树
    kruskal重构树
  • 原文地址:https://www.cnblogs.com/ruhai/p/10840467.html
Copyright © 2020-2023  润新知