Python快速而美丽[v1.0.0][线程池]

线程池

  • 当程序中需要创建大量生存期很短的线程时,应该考虑使用线程池,因为线程的创建成本较高,每次创建都要与系统交互,线程池在系统启动时就创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它,当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待下一个函数
  • 使用线程池可以有效的控制系统中并发线程的数量,线程池的最大线程数就限制了并发的上限
  • 线程池的基类是concurrent.futures模块中的Executor,它提供了两个子类ThreadPoolExecutorProcessPoolExecutor前者用于创建线程池后者用于创建进程池

Executor

它提供了如下常用方法:

  • submit(fn, *args, **kwargs):将fn函数提交给线程池,*args代表传给fn函数的参数,*kwargs代表以关键字参数的形式为fn函数传入参数
  • map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数map(func, *iterables),只是该函数将会为iterables的每一个元素启动一个线程,以并发的形式来执行func函数,以异步方式立即对iterables执行map处理,相当于启动len(iterables)个线程,并返回每个线程的执行结果
  • shutdown(wait=True):关闭线程池,在用完一个线程池后,应该调用其shutdown()方法,该方法将启动线程池的关闭序列,调用了该方法后线程池不再接收新任务,但会将以前所有的已提交任务执行完成,当线程池中所有的任务都执行完成后,该线程池中的所有线程都会死亡

程序将task函数提交给线程池后,submit方法会返回一个Future对象,Future类主要用于获取线程任务函数的返回值,因为线程任务会在新线程中以异步方式执行,因此线程执行的函数相当于一个将来的任务

Future

它提供了如下常用方法:

  • cancel():取消该Future代表的线程任务,如果该任务正在执行,不可取消,则该方法返回False,否则,程序会取消该任务,并返回True
  • cancelled():返回Future代表的线程任务是否被成功取消
  • running():如果该Future代表的线程任务正在执行、不可取消,该方法返回Ture
  • done():如果该Future代表的线程任务被成功取消或者执行完成,则该方法返回Ture
  • result(timeout=None):获取该Future代表的线程任务最后返回的结果,如果Future代表的线程任务还未完成,该方法将会阻塞当前线程,其中timeout参数指定最多阻塞多少秒
  • exception(timeout=None):获取该Future代表的线程任务所引发的异常,如果该任务成功完成,没有异常,则该方法返回None
  • add_done_callback(fn):为该Future代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该fn函数

代码示例

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

  • 调用ThreadPoolExecutor类的构造器创建一个线程池
  • 定义一个普通作为线程任务
  • 调用ThreadPoolExecutor对象的submit()方法来提交线程任务
  • 当不想提交任何任务时,调用ThreadPoolExecutor对象的shutdown()方法来关闭线程池
from concurrent.futures import ThreadPoolExecutor
import threading
import time

# 定义一个准备作为线程任务的函数
def action(max):
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i))
        my_sum += i
    return my_sum
# 创建一个包含2条线程的线程池
pool = ThreadPoolExecutor(max_workers=2)
# 向线程池提交一个task, 50会作为action()函数的参数, 并且返回future对象
future1 = pool.submit(action, 50)
# 向线程池再提交一个task, 100会作为action()函数的参数, 并且返回future对象
future2 = pool.submit(action, 100)
# 判断future1代表的任务是否结束
print(future1.done())
time.sleep(3)
# 判断future2代表的任务是否结束
print(future2.done())
# 查看future1代表的任务返回的结果
print(future1.result())
# 查看future2代表的任务返回的结果
print(future2.result())
# 关闭线程池
pool.shutdown()

需要注意的是,当程序使用Future的result()方法来获取结果时,该方法将会阻塞当前线程,如果没有指定timeout,则当前线程将一直处于阻塞状态,直到Future得到返回结果
如果程序不希望直接调用result()方法阻塞线程,则可通过调用add_done_callback()方法来添加回调函数,该回调函数形如fn(future), 当线程任务完成后,程序会自动触发该回调函数,并将对应的Future对象 作为参数传给该回调函数

from concurrent.futures import ThreadPoolExecutor
import threading
import time

# 定义一个准备作为线程任务的函数
def action(max):
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i))
        my_sum += i
    return my_sum
# 创建一个包含2条线程的线程池
with ThreadPoolExecutor(max_workers=2) as pool:
    # 向线程池提交一个task, 50会作为action()函数的参数
    future1 = pool.submit(action, 50)
    # 向线程池再提交一个task, 100会作为action()函数的参数
    future2 = pool.submit(action, 100)
    def get_result(future):
        print(future.result())
    # 为future1添加线程完成的回调函数
    future1.add_done_callback(get_result)
    # 为future2添加线程完成的回调函数
    future2.add_done_callback(get_result)
    print('--------------')

由于线程池实现了上下文管理协议(Context Manage Protocol),因此,程序可以使用with语句来管理线程池,这样可以避免手动关闭线程池

map

from concurrent.futures import ThreadPoolExecutor
import threading
import time

# 定义一个准备作为线程任务的函数
def action(max):
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i))
        my_sum += i
    return my_sum
# 创建一个包含4条线程的线程池
with ThreadPoolExecutor(max_workers=4) as pool:
    # 使用线程执行map计算
    # 后面元组有3个元素,因此程序启动3条线程来执行action函数
    results = pool.map(action, (50, 100, 150))
    print('--------------')
    for r in results:
        print(r)

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/105689715