python:ThreadPoolExecutor线程池和ProcessPoolExecutor进程池

2023-12-04 11:40:28 浏览数 (3)

为什么需要线程池呢?

代码语言:javascript复制
对于io密集型,提高执行的效率。
线程的创建是需要消耗系统资源的。
每个线程各自分配一个任务,剩下的任务排队等待,
  当某个线程完成了任务的时候,排队任务就可以安排给这个线程继续执行。

如何来实现线程池呢?

代码语言:javascript复制
标准库concurrent.futures模块
  它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,
   分别实现了对threading模块和multiprocessing模块的进一步抽象。
   不仅可以帮我们自动调度线程,还可以做到:
    - 主线程可以获取某一个线程(或者任务)的状态,以及返回值
    - 当一个线程完成的时候,主线程能够立即知道
    - 让多线程和多进程的编码接口一致

如何用呢?简单代码演示


代码语言:javascript复制
from concurrent.futures import ThreadPoolExecutor


def get(run):
    print(" {}finished".format(run))

# 创建线程池
# 设置线程池中最多能同时运行的线程数目,其他等待
executor = ThreadPoolExecutor(max_workers=2)

# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
# task1和task2是任务句柄
task1 = executor.submit( get, (2) )
task2 = executor.submit( get, (3) )

结果展示

代码语言:javascript复制
 2finished
 3finished
代码语言:javascript复制
# done()方法用于判断某个任务是否完成,bool型,完成返回True,没有完成返回False
print( task1.done() )

# cancel()方法用于取消某个任务,该任务没有放到线程池中才能被取消,如果已经放进线程池子中,则不能被取消
# bool型,成功取消了返回True,没有取消返回False
print( task2.cancel() )

# result()方法可以获取task的执行结果,get()函数有返回值
print( task1.result() )
print( task2.result() )

看下最后的展示

代码语言:javascript复制
True
False
None
None

因为没有返回结果,所以打印的是None

代码语言:javascript复制
ThreadPoolExecutor类在构造实例的时候,传入max_workers参数来设置线程池中最多能同时运行的线程数目
使用submit()函数来提交线程需要执行任务(函数名和参数)到线程池中,并返回该任务的句柄,
注意submit()不是阻塞的,而是立即返回。
通过submit()函数返回的任务句柄,能够使用done()方法判断该任务是否结束,使用cancel()方法来取消,
使用result()方法可以获取任务的返回值,查看内部代码,发现该方法是阻塞的

ProcessPoolExecutor多进程


代码语言:javascript复制

from concurrent.futures import ProcessPoolExecutor
def get(run):
    print(" {}finished".format(run))


if __name__ == '__main__':
    p = ProcessPoolExecutor(4)  # 设置

    for i in range(10):
        # 同步调用方式,不仅要调用,还要等返回值
        obj = p.submit(get, "参数")  # 传参方式(任务名,参数),参数使用位置或者关键字参数
        res = obj.result()
    p.shutdown(wait=True)  # 关闭进程池的入口,等待池内任务运行结束
    print("主")

结果打印;

代码语言:javascript复制
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
 参数finished
主

线程池和进程池如何选择呢?

进程池:异步 回调函数,cpu密集型,同时执行,每个进程有不同的解释器和内存空间,互不干扰

代码语言:javascript复制

from concurrent.futures import ProcessPoolExecutor
def get(run):
    print(" {}finished".format(run))

def future(future):
    print(future.result())

if __name__ == '__main__':
    p = ProcessPoolExecutor(4)  # 设置

    for i in range(10):
        # 同步调用方式,不仅要调用,还要等返回值
        obj = p.submit(get, "参数")  # 传参方式(任务名,参数),参数使用位置或者关键字参数
        obj.add_done_callback(future)
    p.shutdown(wait=True)  # 关闭进程池的入口,等待池内任务运行结束
    print("主")

常用的就是我们的爬虫。爬去后解析,解析后存储,会大量使用的cpu。

(2)线程池:异步 回调函数,IO密集型主要使用方式,线程池:执行操作为谁有空谁执行

代码语言:javascript复制
from concurrent.futures import ThreadPoolExecutor
def get(run):
    print(" {}finished".format(run))

def future(future):
    print(future.result())

if __name__ == '__main__':
    p = ThreadPoolExecutor(4)
    start = time.time()
    testcase=[2,3,4,5]
    for case in testcase:
        futu = p.submit(get, case)
        futu.add_done_callback(future)
    p.shutdown(wait=True)

结果展示:

代码语言:javascript复制
 2finished
None
 3finished
 4finished
None
None
 5finished
None

使用:移动端多用例并行执行的时候,需要设备空闲才执行,我们可以用线程来管理一个设备,设备执行完毕 就下发一个任务。这个最好的方式 可以和队列放在一起去执行。

总结

代码语言:javascript复制
线程不是越多越好,会涉及cpu上下文的切换(会把上一次的记录保存)。
进程比线程消耗资源,进程相当于一个工厂,工厂里有很多人,里面的人共同享受着福利资源,一个进程里默认只有一个主线程,
计算密度型适用于多进程
线程:线程是计算机中工作的最小单元
进程:默认有主线程 (帮工作)可以多线程共存

0 人点赞