ThreadPoolExecutor线程池和ProcessPoolExecutor进程池

1,979 阅读3分钟

前言

标准库concurrent.futures模块,

它提供了ProcessPoolExecutor和ThreadPoolExecutor两个类,

实现了对threading和multiprocessing的进一步抽象.

1/ThreadPoolExecutor线程池

ThreadPoolExecutor()类在构造实例的时候,传入max_workers参数来设置线程中最多能同时运行的线程数目

使用submit()函数来提交线程需要执行任务到线程池中,并返回该任务的句柄(类似于文件、画图),注意submit()不是阻塞的,而是立即返回。

通过submit()函数返回的任务句柄,能够使用done()方法判断该任务是否结束

使用result()方法可以获取任务的返回值,查看内部代码,发现这个方法是阻塞的

from concurrent.futures import ThreadPoolExecutor 
import time 


def f(times): 
  print("get page {}s finished".format(times)) 
  return times 

# 构建多线程池子
executor = ThreadPoolExecutor(max_workers=2) 

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

#done方法用于判断某个任务是否完成 
print(task1.done()) 

#cancel方法用于取消某个任务,该任务没有放到线程池中才能被取消
print(task2.cancel()) 

print(task1.done()) 

#result方法可以获取task的执行结果 
print(task1.result()) 

#结果: 
# get page 3s finished 
# get page 2s finished 
# True 
# False 
# True 
# 3

2/ProcessPoolExecutor进程池

有2种任务提交方式

<1>同步提交方式

提交任务,原地等待任务执行结束,拿到任务返回结果,再执行接下来的任务 优点:可以解耦合 缺点:速度慢,因为需要等结果,然后再执行下一个

 
import datetime
from concurrent.futures import ProcessPoolExecutor
from threading import current_thread
import time, random, os
import requests

def f(name):
    print('%s %s is running'%(name,os.getpid()))
    #print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

if __name__ == '__main__':
    # 设置进程池内进程 
    process_pool = ProcessPoolExecutor(4)
    for i in range(10):
        # 同步调用方式,调用和等值
        
        obj = process_pool.submit(f,"进程pid:")

        # .result()函数,得到进程的返回结果,及return的结果
        # 如果f()函数中没有return,则obj.result()是None
        res = obj.result()

    # 关闭进程池的入口,等待池内任务运行结束,再执行主程序
    process_pool.shutdown(wait=True) 

    print("主线程")

# 打印出来的结果,每个进程的pid都是一样的, 说明这是同步的

<2>异步提交方式

只调用,不等值 优点:速度快 缺点:存在耦合

import datetime
from concurrent.futures import ProcessPoolExecutor
from threading import current_thread
import time, random, os
import requests

def f(name): 
    print("%s %s is running" %(name,os.getpid())) 
    time.sleep(random.randint(1,3)) 

if __name__ == '__main__': 
    # 设置进程池内进程 
    process_pool = ProcessPoolExecutor(4)

    for i in range(10): 
        # 异步提交方式,只调用,不等值 
        process_pool.submit(f,'进程pid:') 
        # 传参方式(任务名,参数),参数使用位置参数或者关键字参数

    # 关闭进程池的入口,等待池内任务运行结束,再执行主程序
    process_pool.shutdown( wait=True ) 

    print('主线程')

总结

1、线程不是越多越好,会涉及cpu上下文的切换(会把上一次的记录保存)。

2、进程比线程消耗资源,进程相当于一个工厂,工厂里有很多人,里面的人共同享受着福利资源,,一个进程里默认只有一个主线程,比如:开启程序是进程,里面执行的是线程,线程只是一个进程创建多个人同时去工作。

3、线程里有GIL全局解锁器:不允许cpu调度

4、计算密度型(cpu密集型)适用于多进程
4.1 I/O密集型适合多线程,谁有空谁执行

5、线程:线程是计算机中工作的最小单元

6、进程:默认有主线程 (帮工作)可以多线程共存

7、协程:一个线程,一个进程做多个任务,使用进程中一个线程去做多个任务,微线程

8、GIL全局解释器锁:保证同一时刻只有一个线程被cpu调度