1. 引入ThreadPoolExecutor的原因
ThreadPoolExecutor相比于threading的优点:
- 实现多线程中的每一个线程都可以按照一定的顺序依次完成不同的任务。避免使用threading时每执行一次任务都需重新启动一个线程,完成一个任务销毁一个线程。
- 主线程可以获取子线程(或者任务的)的状态,以及返回值。
- 当一个子线程完成的时候,主线程能够立即知道。
- 让多线程和多进程的编码接口一致。
2. future的实例对象
future的 实例对象
:一个未来可期的对象,通过它主线程(或主进程)可以获悉子线程(或子进程)的状态及返回值。
future的实例对象由ThreadPoolExecutor的实例对象绑定的submit方法返回。
3. 常用方法
3.1 submit、done、result、shutdown
submit
:由ThreadPoolExecutor的实例对象绑定submit。提交需要执行的任务到线程池中,并返回一个future的实例对象。注意 submit() 不是阻塞的,而是立即返回。
done
:由Future的实例对象future绑定done,返回子线程的状态。
result
:由Future的实例对象future绑定result,返回子线程执行的返回值。
shutdown
:由ThreadPoolExecutor的实例对象绑定submit。关闭线程池。当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
shutdownNow
:由ThreadPoolExecutor的实例对象绑定submit。根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。 它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
print(threading.current_thread())
return sleep_time
executor = ThreadPoolExecutor(max_workers=1) # max_workers 参数是控制同时执行的最大任务数
future0 = executor.submit(test, 0.1, 0) # 向线程池中添加第一个任务
future1 = executor.submit(test, 0.2, 0) # 向线程池中添加第二个任务
print(future0.done())
print(future0.result())
print(future1.done())
print(future1.result())
executor.shutdown(wait=True)
"""
运行结果:
False
<Thread(ThreadPoolExecutor-0_0, started daemon 15008)>
0.1
False
<Thread(ThreadPoolExecutor-0_0, started daemon 15008)>
0.2
Process finished with exit code 0
"""
3.2 cancel 、cancelled、running、set_result
cancel
:由Future的实例对象future绑定cancel,取消已提交到线程池但还未开始执行的任务。取消成功返回True,取消失败返回False。
cancelled
:由Future的实例对象future绑定cancel,判断该future绑定的任务是否取消成功。取消成功返回True,取消失败返回False。
running
:由Future的实例对象future绑定running,判断该future绑定的任务是否正在运行。若正则运行返回True,否则返回False。
set_result
:由Future的实例对象future绑定set_result。设置future绑定的任务的返回值,并发送通知 notify。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
print(threading.current_thread())
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future2 = executor.submit(test, 0.1, 0)
future0 = executor.submit(test, 0.3, 0)
future1 = executor.submit(test, 0.2, 0)
print(f"future2_result: {future2.result()}")
print("===========================================")
print(f"future1: {future1.done()}")
print(f"future1_cancel: {future1.cancel()}")
print(f"future1_cancelled:{future1.cancelled()}")
print("===========================================")
print(f"future0: {future0.done()}")
print(f"future0_running: {future0.running()}")
future0.set_result(100)
print(f"future0_running: {future0.running()}")
print(f"future0_result: {future0.result()}") # result是一个可以让主线程处于阻塞状态的函数,但此处不会阻塞,因已经通过set_result为future0设置了一个结果
time.sleep(0.8) # 等待future0所绑定的任务完成
print(f"future0_result: {future0.result()}") # 在future0所绑定的任务完成后,子线程把future0设置为了任务完成后的返回值
executor.shutdown(wait=True)
"""
<Thread(ThreadPoolExecutor-0_0, started daemon 9380)>
future2_result: 0.1
===========================================
future1: False
future1_cancel: True
future1_cancelled:True
===========================================
future0: False
future0_running: True
future0_running: False
future0_result: 100
<Thread(ThreadPoolExecutor-0_0, started daemon 9380)>
future0_result: 0.3
Process finished with exit code 0
"""
3.3 as_completed
问题:上面虽然提供了判断任务是否结束的方法(done),但是不能在主线程中一直判断啊。最好的方法是当某个任务结束了,就给主线程返回结果,而不是一直判断每个任务是否结束。
as_completed
:从concurrent.futures导入as_completed。调用该函数会返回一个生成器对象
。as_completed 在遍历的时候如果有子线程执行完了就会yield执行完的结果,以后的任务执行完一个这里就会返回一个,可以理解为 as_completed 会等待任务执行
,比如我们这里在遍历的时候只有一个执行完了,那就只会打印一个,如果有第二个执行完了,它就会打印第二个,而且这个也不会影响到主线程。
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
print(threading.current_thread())
return sleep_time
executor = ThreadPoolExecutor(max_workers=2)
tasks = [executor.submit(test, sleep_time, 0) for sleep_time in [0.1, 0.2, 0.3]]
for future in as_completed(tasks):
print(future.result())
"""
<Thread(ThreadPoolExecutor-0_0, started daemon 9752)>
0.1
<Thread(ThreadPoolExecutor-0_1, started daemon 11492)>
0.2
<Thread(ThreadPoolExecutor-0_0, started daemon 9752)>
0.3
Process finished with exit code 0
"""
3.5 wait
wait
:从concurrent.futures导入wait。主线程会一直处于阻塞状态,直到传入的所有任务执行完成或阻塞超时。
原型:
wait(fs, timeout=None, return_when=ALL_COMPLETED)
参数:
- fs:表示执行的task序列
- timeout:表示等待的最长时间,超过这个时间即使子线程未执行完成也将返回
- return_when:表示wait返回结果的条件,它接收三个参数FIRST_COMPLETED, FIRST_EXCEPTION 和ALL_COMPLETE,默认设置为ALL_COMPLETED。默认为ALL_COMPLETED全部执行完成再返回
对于timeout和return_when这两个参数,在程序运行过程中先满足哪个条件,程序就按哪个条件来。
返回值:
返回一个tuple(元组),tuple中包含两个set(集合),一个是completed(已完成的)另外一个是uncompleted(未完成的)。
from concurrent.futures import ThreadPoolExecutor, wait
import time
def test(sleep_time, a):
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=2)
tasks = [executor.submit(test, sleep_time, 0) for sleep_time in [0.1, 0.2, 0.3]]
done_futures, not_done_futures = wait(tasks, 0.2, return_when="ALL_COMPLETED")
print(f"已完成:{done_futures}")
print(f"未完成:{not_done_futures}")
for future in done_futures:
print(future.result())
"""
运行结果:
已完成:{<Future at 0x24bf361c710 state=finished returned float>, <Future at 0x24bf3669c18 state=finished returned float>}
未完成:{<Future at 0x24bf3678f28 state=running>}
0.1
0.2
Process finished with exit code 0
"""
3.6 map
map
:由ThreadPoolExecutor的实例对象绑定map。与普通map用法相同。
作用:类似于submit + wait,但返回值不同,wait返回一个包含done_future集合和not_done_future任务集合的元组,而map直接返回一个所有任务完成后的返回值列表。
原型:
map(fn, *iterables, timeout=None),
参数:
- fn:线程执行的函数;
- iterables:接受一个可迭代对象;
- timeout:跟wait()的timeout一样,但如果timeout小于线程执行时间会抛异常TimeoutError。
返回值:
返回一个生成器,该生成器迭代出的结果顺序与iterables参数的顺序一致。
from concurrent.futures import ThreadPoolExecutor
import time
def test(sleep_time_a):
sleep_time = sleep_time_a[0]
a = sleep_time_a[1]
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=2)
tasks = [(sleep_time, 0) for sleep_time in [0.1, 0.2, 0.3]]
results = executor.map(test, tasks, timeout=0.5) # timeout=0.2后报TimeoutError。
print(list(results))
"""
运行结果:
[0.1, 0.2, 0.3]
Process finished with exit code 0
"""
3.7 add_done_callback
add_done_callback
:由Future的实例对象future绑定add_done_callback。
方法被触发时间:future所绑定的任务运行完成或 被cancel 后调用。使用的子线程与future所绑定任务使用的线程是同一线程。
该方法与result的区别:
- 程序调用了 future所绑定的 result() 方法来获取线程任务的返回值,但该方法会阻塞当前主线程,只有等到钱程任务完成后,result() 方法的阻塞才会被解除。
- 如果程序不希望直接调用 result() 方法阻塞线程,则可通过 future所绑定的 add_done_callback() 方法来添加回调函数,该回调函数形如 fn(future)。
原型:
add_done_callback(self, fn)
参数:
- fn:所需注册的回调函数,该回调函数的参数当子线程任务完成后,程序会自动触发该回调函数,并将对应的 future作为参数传给该回调函数。
from concurrent.futures import ThreadPoolExecutor, wait
import threading
import time
def test(sleep_time, a):
print("=============test_start=============")
time.sleep(sleep_time)
print(threading.current_thread())
print("=============test_end=============")
return sleep_time
def call_back(future):
print("+++++++++++call_back_start+++++++++++")
print(threading.current_thread())
print(future.result())
print("+++++++++++call_back_end+++++++++++")
executor = ThreadPoolExecutor(max_workers=1)
future0 = executor.submit(test, 0.1, 0)
future0.add_done_callback(call_back) # 不会阻塞主线程去等待子线程的完成
print(f"===================main==================")
executor.shutdown(wait=True)
"""=============test_start=============
===================main==================
<Thread(ThreadPoolExecutor-0_0, started daemon 12496)>
=============test_end=============
+++++++++++call_back_start+++++++++++
<Thread(ThreadPoolExecutor-0_0, started daemon 12496)>
0.1
+++++++++++call_back_end+++++++++++
Process finished with exit code 0
运行结果:
"""
3.8 exception
exception
:由Future的实例对象future绑定exception。
原型:
exception(self, timeout=None)
参数:
- timeout:希望在给定的时间内完成future所绑定任务。
返回值:
若 future所绑定任务运行期间若出现异常,则返回异常信息;没有异常则返回None。
抛出异常两种情况:
- CancelledError: 如果 future所绑定任务被cancel
- TimeoutError: 如果 future所绑定任务在给定的时间没有执行完毕
# uture所绑定任务运行期间若出现异常,则返回异常信息
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
def test_error(sleep_time, a):
b = 3/0
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(test, 0.1, 0)
future_error = executor.submit(test_error, 0.2, 0)
print(future.result())
print(future.exception())
print(future_error.exception())
executor.shutdown(wait=True)
"""
运行结果:
<Thread(ThreadPoolExecutor-0_0, started daemon 11100)>
0.1
None
division by zero
Process finished with exit code 0
"""
# 抛出异常:CancelledError: 如果 future所绑定任务被cancel
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
def test_cancel(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(test, 0.1, 0)
future_error = executor.submit(test_cancel, 0.2, 0)
print(future_error.cancel())
print(future_error.exception())
executor.shutdown(wait=True)
"""
运行结果:
<Thread(ThreadPoolExecutor-0_0, started daemon 14924)>
Traceback (most recent call last):
True
File "E:/Project/PyCharm/test/Account.py", line 22, in <module>
print(future_error.exception())
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 456, in exception
raise CancelledError()
concurrent.futures._base.CancelledError
Process finished with exit code 1
"""
# 抛出异常:TimeoutError: 如果 future所绑定任务在给定的时间没有执行完毕
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
def test_timeout(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(test, 0.1, 0)
future_timeout = executor.submit(test_timeout, 0.2, 0)
print(future_timeout.exception(timeout=0.1))
executor.shutdown(wait=True)
"""
运行结果:
<Thread(ThreadPoolExecutor-0_0, started daemon 4684)>
<Thread(ThreadPoolExecutor-0_0, started daemon 4684)>
Traceback (most recent call last):
File "E:/Project/PyCharm/test/Account.py", line 21, in <module>
print(future_timeout.exception(timeout=0.1))
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 467, in exception
raise TimeoutError()
concurrent.futures._base.TimeoutError
Process finished with exit code 1
"""
3.9 set_exception
set_exception
:由Future的实例对象future绑定set_exception。为future设置给定的异常值,类似于set_result。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
print(threading.current_thread())
time.sleep(sleep_time)
return sleep_time
def test_error(sleep_time, a):
time.sleep(sleep_time)
b = 3/0
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(test, 0.1, 0)
future_error = executor.submit(test_error, 0.2, 0)
time.sleep(0.3) # 等待future所绑定任务运行过程中出错
# 在future所绑定任务运行过程中出错才能使用set_exception,否则会报错RuntimeError: Future in unexpected state
future_error.set_exception("Exception")
print(future_error.exception())
executor.shutdown(wait=True)
"""
运行结果:
<Thread(ThreadPoolExecutor-0_0, started daemon 14564)>
Exception
Process finished with exit code 0
"""
3.10 set_running_or_notify_cancel
set_running_or_notify_cancel
:由Future的实例对象future绑定set_running_or_notify_cancel。
标记future为 RUNNING 或者 CANCELLED_AND_NOTIFIED,
- 1、如果future已经 cancelled 则期物任何等待执行的线程都会被 notify 并且 return False。
- 2、如果future没有被 cancelled,则状态变更为 RUNNING,返回 True。
- 3、此方法应该在future所关联的任务执行前被调用,如果此方法返回 False,那么任务不应该被执行。
Returns:
如果期物已经被 cancelled,返回 False;其他情况返回 True
Raises:
RuntimeError:如果此方法已经被调用或者 set_result() 或者 set_exception()被调用。
# 如果future没有被 cancelled,则状态变更为 RUNNING,返回 True。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future0 = executor.submit(test, 0.1, 0)
future1 = executor.submit(test, 0.2, 0)
print(future1.set_running_or_notify_cancel())
executor.shutdown(wait=True)
"""
运行结果:
True
Future 2183270866056 in unexpected state: RUNNING
Exception in worker
Traceback (most recent call last):
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 69, in _worker
work_item.run()
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 52, in run
if not self.future.set_running_or_notify_cancel():
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 508, in set_running_or_notify_cancel
raise RuntimeError('Future in unexpected state')
RuntimeError: Future in unexpected state
Process finished with exit code 0
"""
# 如果future已经 cancelled 则期物任何等待执行的线程都会被 notify 并且 return False。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future0 = executor.submit(test, 0.1, 0)
future1 = executor.submit(test, 0.2, 0)
print(future1.cancel())
print(future1.set_running_or_notify_cancel())
executor.shutdown(wait=True)
"""
运行结果:
True
False
Future 2272270527232 in unexpected state: CANCELLED_AND_NOTIFIED
Exception in worker
Traceback (most recent call last):
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 69, in _worker
work_item.run()
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 52, in run
if not self.future.set_running_or_notify_cancel():
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 508, in set_running_or_notify_cancel
raise RuntimeError('Future in unexpected state')
RuntimeError: Future in unexpected state
Process finished with exit code 0
"""
# RuntimeError:如果此方法已经被调用或者 set_result() 或者 set_exception()被调用。
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def test(sleep_time, a):
time.sleep(sleep_time)
return sleep_time
executor = ThreadPoolExecutor(max_workers=1)
future0 = executor.submit(test, 0.1, 0)
future1 = executor.submit(test, 0.2, 0)
print(future1.set_result(10))
print(future1.set_running_or_notify_cancel())
executor.shutdown(wait=True)
"""
运行结果:
Future 1931723647744 in unexpected state: FINISHED
None
Traceback (most recent call last):
File "E:/Project/PyCharm/test/Account.py", line 15, in <module>
print(future1.set_running_or_notify_cancel())
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 508, in set_running_or_notify_cancel
raise RuntimeError('Future in unexpected state')
RuntimeError: Future in unexpected state
Future 1931723647744 in unexpected state: FINISHED
Exception in worker
Traceback (most recent call last):
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 69, in _worker
work_item.run()
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\thread.py", line 52, in run
if not self.future.set_running_or_notify_cancel():
File "D:\Program Files\Python3.6.6\lib\concurrent\futures\_base.py", line 508, in set_running_or_notify_cancel
raise RuntimeError('Future in unexpected state')
RuntimeError: Future in unexpected state
Process finished with exit code 1
"""