线程 — 基于线程的并行 — Python 文档
threading — 基于线程的并行性
源代码: :source:`Lib/threading.py`
该模块在较低级别的 _thread 模块的顶部构造更高级别的线程接口。 另请参阅 queue 模块。
3.7 版本变更: 该模块以前是可选的,现在一直可用。
笔记
虽然下面没有列出它们,但该模块仍然支持 Python 2.x 系列中该模块中某些方法和函数使用的 camelCase
名称。
该模块定义了以下功能:
- threading.active_count()
- 返回当前活动的 Thread 对象的数量。 返回的计数等于 enumerate() 返回的列表的长度。
- threading.current_thread()
- 返回当前的 Thread 对象,对应于调用者的控制线程。 如果调用者的控制线程不是通过 threading 模块创建的,则返回一个功能有限的虚拟线程对象。
- threading.excepthook(args, /)
处理由 Thread.run() 引发的未捕获异常。
args 参数具有以下属性:
exc_type:异常类型。
exc_value:异常值,可以是
None
。exc_traceback:异常回溯,可以是
None
。thread:引发异常的线程,可以是
None
。
如果 exc_type 是 SystemExit,异常会被静默忽略。 否则,异常会打印在 sys.stderr 上。
如果此函数引发异常,则会调用 sys.excepthook() 来处理它。
threading.excepthook() 可以被覆盖以控制如何处理 Thread.run() 引发的未捕获异常。
使用自定义钩子存储 exc_value 可以创建引用循环。 当不再需要异常时,应该明确清除它以中断引用循环。
如果将 thread 设置为正在完成的对象,则使用自定义钩子存储 thread 可以使其复活。 避免在自定义钩子完成后存储 thread 以避免复活对象。
也可以看看
sys.excepthook() 处理未捕获的异常。
3.8 版中的新功能。
- threading.get_ident()
返回当前线程的“线程标识符”。 这是一个非零整数。 它的价值没有直接的意义; 它旨在用作魔术饼干,例如 索引线程特定数据的字典。 当一个线程退出并创建另一个线程时,线程标识符可能会被回收。
3.3 版中的新功能。
- threading.get_native_id()
返回内核分配的当前线程的本机整数线程 ID。 这是一个非负整数。 它的值可用于在系统范围内唯一标识此特定线程(直到线程终止,之后该值可由操作系统回收)。
3.8 版中的新功能。
- threading.enumerate()
- 返回当前活动的所有 Thread 对象的列表。 该列表包括由 current_thread() 创建的守护线程和虚拟线程对象。 它不包括终止的线程和尚未启动的线程。 但是,主线程始终是结果的一部分,即使在终止时也是如此。
- threading.main_thread()
返回主要的 Thread 对象。 在正常情况下,主线程是启动 Python 解释器的线程。
3.4 版中的新功能。
- threading.settrace(func)
- 为从 threading 模块启动的所有线程设置跟踪函数。 func 将在每个线程的 run() 方法被调用之前传递给 sys.settrace()。
- threading.setprofile(func)
- 为从 threading 模块启动的所有线程设置配置文件函数。 func 将在每个线程的 run() 方法被调用之前传递给 sys.setprofile()。
- threading.stack_size([size])
- 返回创建新线程时使用的线程堆栈大小。 可选的 size 参数指定用于后续创建的线程的堆栈大小,并且必须为 0(使用平台或配置的默认值)或至少为 32,768 (32 KiB) 的正整数值。 如果未指定 size,则使用 0。 如果不支持更改线程堆栈大小,则会引发 RuntimeError。 如果指定的堆栈大小无效,则会引发 ValueError 并且堆栈大小未修改。 32 KiB 目前是支持的最小堆栈大小值,以保证解释器本身有足够的堆栈空间。 请注意,某些平台可能对堆栈大小的值有特定限制,例如要求最小堆栈大小 > 32 KiB 或要求以系统内存页面大小的倍数进行分配 - 应参考平台文档以获取更多信息(4 KiB 页很常见;如果没有更具体的信息,建议使用 4096 的倍数作为堆栈大小)。
该模块还定义了以下常量:
- threading.TIMEOUT_MAX
阻塞函数的 timeout 参数允许的最大值 (Lock.acquire(), RLock.acquire(), Condition.wait() 等)。 指定大于此值的超时将引发 OverflowError。
3.2 版中的新功能。
该模块定义了许多类,在下面的部分中进行了详细说明。
该模块的设计松散地基于 Java 的线程模型。 然而,Java 使锁和条件变量成为每个对象的基本行为,它们在 Python 中是独立的对象。 Python 的 Thread 类支持 Java 的 Thread 类的行为子集; 目前,没有优先级,没有线程组,线程不能被销毁、停止、暂停、恢复或中断。 Java Thread 类的静态方法在实现时被映射到模块级函数。
下面描述的所有方法都是以原子方式执行的。
线程本地数据
线程本地数据是其值是线程特定的数据。 要管理线程本地数据,只需创建 local(或子类)的实例并在其上存储属性:
mydata = threading.local()
mydata.x = 1
对于不同的线程,实例的值会有所不同。
- class threading.local
表示线程本地数据的类。
有关更多详细信息和大量示例,请参阅
_threading_local
模块的文档字符串。
线程对象
Thread 类表示在单独的控制线程中运行的活动。 有两种方法可以指定活动:通过将可调用对象传递给构造函数,或通过覆盖子类中的 run() 方法。 不应在子类中覆盖其他方法(构造函数除外)。 换句话说,only 覆盖了这个类的 __init__()
和 run() 方法。
创建线程对象后,必须通过调用线程的 start() 方法启动其活动。 这会在单独的控制线程中调用 run() 方法。
一旦线程的活动开始,线程就被认为是“活动的”。 当它的 run() 方法终止时,它停止活动——正常情况下,或者通过引发未处理的异常。 is_alive() 方法测试线程是否处于活动状态。
其他线程可以调用线程的 join() 方法。 这会阻塞调用线程,直到调用其 join() 方法的线程终止。
一个线程有一个名字。 名称可以传递给构造函数,并通过 name 属性读取或更改。
如果 run() 方法引发异常,则会调用 threading.excepthook() 来处理它。 默认情况下,threading.excepthook() 静默忽略 SystemExit。
一个线程可以被标记为“守护线程”。 这个标志的意义在于,当只剩下守护线程时,整个 Python 程序就会退出。 初始值是从创建线程继承的。 该标志可以通过 daemon 属性或 daemon 构造函数参数设置。
有一个“主线程”对象; 这对应于 Python 程序中的初始控制线程。 它不是守护线程。
有可能创建“虚拟线程对象”。 这些是与“外来线程”相对应的线程对象,它们是在线程模块之外启动的控制线程,例如直接从 C 代码启动。 虚拟线程对象的功能有限; 它们总是被认为是活着的和守护进程,并且不能被 join()ed。 它们永远不会被删除,因为不可能检测到外来线程的终止。
- class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
应始终使用关键字参数调用此构造函数。 参数是:
组应为
None
; 为将来实现ThreadGroup
类时的扩展保留。target 是由 run() 方法调用的可调用对象。 默认为
None
,意味着什么都不调用。name 是线程名称。 默认情况下,唯一名称的构造形式为“Thread-N”,其中 N 是一个小十进制数。
args 是目标调用的参数元组。 默认为
()
。kwargs 是目标调用的关键字参数字典。 默认为
{}
。如果不是
None
,则 daemon 明确设置线程是否是守护进程。 如果是None
(默认值),守护进程属性是从当前线程继承的。如果子类覆盖了构造函数,它必须确保在对线程执行任何其他操作之前调用基类构造函数 (
Thread.__init__()
)。在 3.3 版更改: 添加了 守护进程 参数。
- start()
启动线程的活动。
每个线程对象最多必须调用一次。 它安排在单独的控制线程中调用对象的 run() 方法。
如果在同一个线程对象上多次调用此方法,则会引发 RuntimeError。
- run()
表示线程活动的方法。
您可以在子类中覆盖此方法。 标准的 run() 方法调用传递给对象构造函数的可调用对象作为 target 参数,如果有的话,位置和关键字参数取自 args和 kwargs 参数,分别。
- join(timeout=None)
等待线程终止。 这会阻塞调用线程,直到调用其 join() 方法的线程终止 - 通常或通过未处理的异常 - 或直到发生可选超时。
当 timeout 参数存在而不是
None
时,它应该是一个浮点数,以秒(或其分数)为单位指定操作超时。 由于 join() 总是返回None
,你必须在 join() 之后调用 is_alive() 来决定是否发生超时 - 如果线程仍然存在,join() 调用超时。当 timeout 参数不存在或
None
时,操作将阻塞,直到线程终止。一个线程可以被 join()ed 多次。
join() 如果尝试加入当前线程,则会引发 RuntimeError,因为这会导致死锁。 在线程启动之前 join() 线程也是一个错误,并且尝试这样做会引发相同的异常。
- name
仅用于识别目的的字符串。 它没有语义。 多个线程可能会被赋予相同的名称。 初始名称由构造函数设置。
- getName()
setName() name 的旧 getter/setter API; 直接将其用作属性。
- ident
此线程的“线程标识符”或
None
(如果该线程尚未启动)。 这是一个非零整数。 请参阅 get_ident() 函数。 当一个线程退出并创建另一个线程时,线程标识符可能会被回收。 即使在线程退出后,标识符仍然可用。
- native_id
此线程的本机完整线程 ID。 这是一个非负整数,如果线程尚未启动,则为
None
。 请参阅 get_native_id() 函数。 这表示由操作系统(内核)分配给线程的线程 ID (TID
)。 它的值可用于在系统范围内唯一标识此特定线程(直到线程终止,之后该值可由操作系统回收)。笔记
与进程 ID 类似,线程 ID 仅从创建线程到线程终止期间有效(保证在系统范围内唯一)。
3.8 版中的新功能。
- is_alive()
返回线程是否存活。
该方法在 run() 方法开始之前返回
True
,直到 run() 方法终止之后。 模块函数 enumerate() 返回所有活动线程的列表。
- daemon
一个布尔值,指示此线程是否为守护线程 (True) 或不是 (False)。 这必须在调用 start() 之前设置,否则会引发 RuntimeError。 它的初始值是从创建线程继承的; 主线程不是守护线程,因此在主线程中创建的所有线程默认为 daemon =
False
。当没有存活的非守护线程时,整个 Python 程序就会退出。
- isDaemon()
setDaemon() daemon 的旧 getter/setter API; 直接将其用作属性。
锁定对象
原语锁是一种同步原语,锁定时不属于特定线程。 在 Python 中,它是目前可用的最低级同步原语,由 _thread 扩展模块直接实现。
原始锁处于“锁定”或“解锁”两种状态之一。 它是在解锁状态下创建的。 它有两个基本方法,acquire() 和 release()。 当状态解锁时, acquire() 将状态更改为锁定并立即返回。 当状态被锁定时,acquire() 阻塞,直到另一个线程中对 release() 的调用将其更改为解锁,然后 acquire() 调用重置它锁定并返回。 release() 方法只能在锁定状态下调用; 它将状态更改为解锁并立即返回。 如果尝试释放未锁定的锁,则会引发 RuntimeError。
锁还支持 上下文管理协议 。
当acquire()中多个线程被阻塞等待状态变为unlocked时,当release()调用将状态重置为unlocked时,只有一个线程继续执行; 未定义等待线程中的哪一个进行,并且可能因实现而异。
所有方法都是原子执行的。
- class threading.Lock
实现原始锁对象的类。 一旦一个线程获得了一个锁,后续的获得它的尝试就会阻塞,直到它被释放; 任何线程都可以释放它。
请注意,
Lock
实际上是一个工厂函数,它返回平台支持的具体 Lock 类的最有效版本的实例。- acquire(blocking=True, timeout=- 1)
获取锁,阻塞或非阻塞。
当调用 blocking 参数设置为
True
(默认值)时,阻塞直到锁被解锁,然后将其设置为锁定并返回True
。当调用 blocking 参数设置为
False
时,不要阻塞。 如果 blocking 设置为True
的调用会阻塞,则立即返回False
; 否则,设置锁为locked并返回True
。在将浮点 timeout 参数设置为正值的情况下调用时,最多阻塞 timeout 指定的秒数,并且只要无法获取锁。
-1
的 timeout 参数指定无限等待。 当 blocking 为 false 时,禁止指定 timeout。如果成功获取锁,则返回值为
True
,否则返回值为False
(例如,如果 timeout 已过期)。3.2 版更改: timeout 参数是新的。
3.2 版更改: 如果底层线程实现支持,现在可以被 POSIX 上的信号中断获取锁。
- release()
释放锁。 这可以从任何线程调用,而不仅仅是获取锁的线程。
当锁被锁定时,将其重置为解锁,然后返回。 如果任何其他线程在等待锁定解锁时被阻塞,则只允许其中一个线程继续执行。
当在解锁的锁上调用时,会引发 RuntimeError。
没有返回值。
- locked()
如果获得了锁,则返回 true。
RLock 对象
重入锁是一个同步原语,可以被同一个线程多次获取。 在内部,除了原始锁使用的锁定/解锁状态之外,它还使用“拥有线程”和“递归级别”的概念。 在锁定状态下,某个线程拥有锁; 在解锁状态下,没有线程拥有它。
为了锁定锁,线程调用其 acquire() 方法; 一旦线程拥有锁,它就会返回。 要解锁锁,线程调用其 release() 方法。 acquire()/release() 调用对可以嵌套; 只有最后的 release()(最外面对的 release())将锁重置为解锁状态,并允许在 acquire() 中阻塞的另一个线程继续进行.
可重入锁还支持 [X33X] 上下文管理协议 。
- class threading.RLock
此类实现可重入锁对象。 重入锁必须由获取它的线程释放。 一旦一个线程获得了可重入锁,同一个线程就可以再次获得它而不会阻塞; 线程每次获得它时都必须释放一次。
请注意,
RLock
实际上是一个工厂函数,它返回平台支持的具体 RLock 类的最有效版本的实例。- acquire(blocking=True, timeout=- 1)
获取锁,阻塞或非阻塞。
不带参数调用时:如果此线程已拥有锁,则将递归级别增加 1,并立即返回。 否则,如果另一个线程拥有锁,则阻塞直到锁被解锁。 一旦锁被解锁(不属于任何线程),然后获取所有权,将递归级别设置为 1,然后返回。 如果有多个线程在等待锁被解锁之前被阻塞,则一次只有一个线程能够获取锁的所有权。 在这种情况下没有返回值。
在 blocking 参数设置为 true 的情况下调用时,执行与不带参数调用时相同的操作,并返回
True
。当在 blocking 参数设置为 false 的情况下调用时,不要阻塞。 如果没有参数的调用会阻塞,立即返回
False
; 否则,执行与不带参数调用时相同的操作,并返回True
。在将浮点 timeout 参数设置为正值的情况下调用时,最多阻塞 timeout 指定的秒数,并且只要无法获取锁。 如果已获取锁,则返回
True
,如果超时已过,则返回 false。3.2 版更改: timeout 参数是新的。
- release()
释放锁,递减递归级别。 如果递减后它为零,则将锁重置为解锁(不属于任何线程),并且如果任何其他线程被阻塞等待锁解锁,则只允许其中一个线程继续进行。 如果递减后递归级别仍然不为零,则锁保持锁定并由调用线程拥有。
仅当调用线程拥有锁时才调用此方法。 如果在解锁时调用此方法,则会引发 RuntimeError。
没有返回值。
条件对象
条件变量总是与某种锁相关联; 这可以传入或默认创建一个。 当多个条件变量必须共享同一个锁时,传入一个很有用。 锁是条件对象的一部分:您不必单独跟踪它。
条件变量遵循 上下文管理协议 :使用 with
语句在封闭块的持续时间内获取关联的锁。 acquire() 和 release() 方法也调用了关联锁的对应方法。
必须在持有相关锁的情况下调用其他方法。 wait() 方法释放锁,然后阻塞,直到另一个线程通过调用 notify() 或 notify_all() 唤醒它。 一旦被唤醒,wait() 重新获取锁并返回。 也可以指定超时。
notify() 方法唤醒等待条件变量的线程之一(如果有任何等待)。 notify_all() 方法唤醒所有等待条件变量的线程。
注意:notify() 和 notify_all() 方法不会释放锁; 这意味着被唤醒的线程不会立即从它们的 wait() 调用中返回,而是只有在调用 notify() 或 notify_all() 的线程时才返回] 最后放弃锁的所有权。
使用条件变量的典型编程风格使用锁来同步对某些共享状态的访问; 对特定状态更改感兴趣的线程重复调用 wait() 直到他们看到所需的状态,而修改状态的线程调用 notify() 或 notify_all( ) 当他们以这样一种方式改变状态时,它可能是其中一个服务员想要的状态。 例如,以下代码是具有无限缓冲区容量的通用生产者-消费者情况:
# Consume one item
with cv:
while not an_item_is_available():
cv.wait()
get_an_available_item()
# Produce one item
with cv:
make_an_item_available()
cv.notify()
while
循环检查应用程序的条件是必要的,因为 wait() 可以在任意长时间后返回,并且提示 notify() 调用的条件可能不再成立。 这是多线程编程所固有的。 wait_for() 方法可用于自动进行条件检查,并简化超时计算:
# Consume an item
with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
要在 notify() 和 notify_all() 之间进行选择,请考虑一种状态更改是否只对一个或多个等待线程感兴趣。 例如 在典型的生产者-消费者情况下,将一项添加到缓冲区只需要唤醒一个消费者线程。
- class threading.Condition(lock=None)
此类实现条件变量对象。 条件变量允许一个或多个线程等待,直到它们被另一个线程通知。
如果给出了 lock 参数而不是
None
,则它必须是 Lock 或 RLock 对象,并用作底层锁. 否则,将创建一个新的 RLock 对象并将其用作底层锁。3.3 版更改: 从工厂函数更改为类。
- acquire(*args)
获取底层锁。 该方法调用底层锁上的相应方法; 返回值是该方法返回的任何值。
- release()
释放底层锁。 该方法调用底层锁上的相应方法; 没有返回值。
- wait(timeout=None)
等到通知或直到发生超时。 如果调用此方法时调用线程尚未获取锁,则会引发 RuntimeError。
此方法释放底层锁,然后阻塞直到它被另一个线程中的相同条件变量的 notify() 或 notify_all() 调用唤醒,或者直到可选超时发生。 一旦被唤醒或超时,它会重新获取锁并返回。
当 timeout 参数存在而不是
None
时,它应该是一个浮点数,以秒(或其分数)为单位指定操作超时。当底层锁是 RLock 时,它不会使用其 release() 方法释放,因为当它被多次递归获取时,这可能实际上不会解锁锁。 取而代之的是,使用了 RLock 类的内部接口,即使在多次递归获取时,它也能真正解锁它。 然后使用另一个内部接口在重新获取锁时恢复递归级别。
返回值是
True
,除非给定的 timeout 过期,在这种情况下它是False
。3.2 版本更改: 以前,该方法总是返回
None
。
- wait_for(predicate, timeout=None)
等到条件评估为真。 predicate 应该是一个可调用的结果将被解释为一个布尔值。 可以提供一个 timeout,给出最长的等待时间。
这个实用方法可能会重复调用 wait() 直到满足谓词,或者直到发生超时。 返回值是谓词的最后一个返回值,如果方法超时,它将评估为
False
。忽略超时特性,调用这个方法大致相当于写:
while not predicate(): cv.wait()
因此,应用与 wait() 相同的规则:调用时必须持有锁,并在返回时重新获取。 谓词在持有锁的情况下进行评估。
3.2 版中的新功能。
- notify(n=1)
默认情况下,唤醒一个等待此条件的线程(如果有)。 如果调用此方法时调用线程尚未获取锁,则会引发 RuntimeError。
该方法最多唤醒n个等待条件变量的线程; 如果没有线程在等待,则为空操作。
如果至少有 n 个线程在等待,当前实现会准确唤醒 n 个线程。 但是,依赖这种行为是不安全的。 未来的优化实现可能偶尔会唤醒超过 n 个线程。
注意:被唤醒的线程实际上不会从它的 wait() 调用中返回,直到它可以重新获取锁。 由于 notify() 不释放锁,它的调用者应该。
- notify_all()
唤醒所有在此条件下等待的线程。 此方法的作用类似于 notify(),但唤醒所有等待线程而不是一个。 如果调用此方法时调用线程尚未获取锁,则会引发 RuntimeError。
信号量对象
这是计算机科学史上最古老的同步原语之一,由早期的荷兰计算机科学家 Edsger W. Dijkstra(他使用名称 P()
和 V()
而不是 acquire() 和 release())。
信号量管理一个内部计数器,该计数器由每个 acquire() 调用递减,并由每个 release() 调用递增。 计数器永远不会低于零; 当 acquire() 发现它为零时,它会阻塞,等待其他线程调用 release()。
信号量还支持 上下文管理协议 。
- class threading.Semaphore(value=1)
这个类实现了信号量对象。 信号量管理一个原子计数器,表示 release() 调用的数量减去 acquire() 调用的数量,再加上一个初始值。 acquire() 方法在必要时阻塞,直到它可以在不使计数器为负的情况下返回。 如果没有给出,value 默认为 1。
可选参数给出内部计数器的初始 值 ; 默认为
1
。 如果给定的 值 小于 0,则会引发 ValueError。3.3 版更改: 从工厂函数更改为类。
- acquire(blocking=True, timeout=None)
获取信号量。
不带参数调用时:
如果内部计数器在进入时大于零,则将其减一并立即返回
True
。如果内部计数器在进入时为零,则阻塞直到被调用 release() 唤醒。 一旦唤醒(并且计数器大于 0),将计数器减 1 并返回
True
。 每次调用 release() 都会唤醒一个线程。 不应依赖线程被唤醒的顺序。
当在 blocking 设置为 false 的情况下调用时,不要阻止。 如果没有参数的调用会阻塞,立即返回
False
; 否则,执行与不带参数调用时相同的操作,并返回True
。当使用
None
以外的 timeout 调用时,它最多会阻塞 timeout 秒。 如果在该时间间隔内未成功完成获取,则返回False
。 否则返回True
。3.2 版更改: timeout 参数是新的。
- release(n=1)
释放信号量,将内部计数器增加 n。 当它在进入时为零并且其他线程正在等待它再次变得大于零时,唤醒这些线程的 n。
3.9 版更改: 增加了 n 参数,可以一次性释放多个等待线程。
- class threading.BoundedSemaphore(value=1)
实现有界信号量对象的类。 有界信号量检查以确保其当前值不超过其初始值。 如果是,则引发 ValueError。 在大多数情况下,信号量用于保护容量有限的资源。 如果信号量被释放太多次,则表明存在错误。 如果没有给出,value 默认为 1。
3.3 版更改: 从工厂函数更改为类。
信号量示例
信号量通常用于保护容量有限的资源,例如数据库服务器。 在资源大小固定的任何情况下,您都应该使用有界信号量。 在产生任何工作线程之前,您的主线程将初始化信号量:
maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)
一旦产生,工作线程在需要连接到服务器时调用信号量的获取和释放方法:
with pool_sema:
conn = connectdb()
try:
# ... use connection ...
finally:
conn.close()
有界信号量的使用降低了导致信号量释放多于获取量的编程错误未被检测到的可能性。
事件对象
这是线程之间最简单的通信机制之一:一个线程发出事件信号,其他线程等待它。
事件对象管理一个内部标志,可以使用 set() 方法将其设置为 true,并使用 clear() 方法重置为 false。 wait() 方法阻塞,直到标志为真。
- class threading.Event
实现事件对象的类。 事件管理一个标志,该标志可以使用 set() 方法设置为 true,并使用 clear() 方法重置为 false。 wait() 方法阻塞,直到标志为真。 该标志最初为假。
3.3 版更改: 从工厂函数更改为类。
- is_set()
当且仅当内部标志为真时返回
True
。
- set()
将内部标志设置为 true。 所有等待它变为真的线程都被唤醒。 一旦标志为真,调用 wait() 的线程根本不会阻塞。
- wait(timeout=None)
阻塞直到内部标志为真。 如果内部标志在进入时为真,则立即返回。 否则,阻塞直到另一个线程调用 set() 将标志设置为 true,或者直到发生可选超时。
当 timeout 参数存在而不是
None
时,它应该是一个浮点数,以秒(或其分数)为单位指定操作超时。此方法返回
True
当且仅当内部标志已设置为 true 时,无论是在等待调用之前还是等待开始之后,因此它将始终返回True
除非给出超时并且操作超时。3.1 更改: 以前,该方法总是返回
None
。
定时器对象
这个类代表一个只有在经过一定时间后才应该运行的动作——一个计时器。 Timer 是 Thread 的子类,因此也可以作为创建自定义线程的示例。
定时器与线程一样,通过调用它们的 start()
方法启动。 可以通过调用 cancel() 方法来停止计时器(在其动作开始之前)。 计时器在执行其操作之前将等待的时间间隔可能与用户指定的时间间隔不完全相同。
例如:
def hello():
print("hello, world")
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
- class threading.Timer(interval, function, args=None, kwargs=None)
创建一个计时器,在 interval 秒过去后,使用参数 args 和关键字参数 kwargs 运行 function。 如果 args 是
None
(默认值),则将使用空列表。 如果 kwargs 是None
(默认值),则将使用空字典。3.3 版更改: 从工厂函数更改为类。
- cancel()
停止定时器,并取消定时器动作的执行。 这仅在计时器仍处于等待阶段时才有效。
障碍物
3.2 版中的新功能。
此类提供了一个简单的同步原语,供需要相互等待的固定数量的线程使用。 每个线程都试图通过调用 wait() 方法来通过屏障,并且会一直阻塞,直到所有线程都进行了 wait() 调用。 此时,线程同时释放。
对于相同数量的线程,屏障可以重复使用任意次数。
例如,这里有一个简单的方法来同步客户端和服务器线程:
b = Barrier(2, timeout=5)
def server():
start_server()
b.wait()
while True:
connection = accept_connection()
process_server_connection(connection)
def client():
b.wait()
while True:
connection = make_connection()
process_client_connection(connection)
- class threading.Barrier(parties, action=None, timeout=None)
为 方 个线程创建屏障对象。 action,当提供时,是一个可调用的,当它们被释放时由其中一个线程调用。 timeout 是默认超时值,如果没有为 wait() 方法指定。
- wait(timeout=None)
通过障碍。 当屏障的所有线程都调用了这个函数时,它们都被同时释放。 如果提供了 timeout,则它优先于提供给类构造函数的任何内容使用。
返回值是 0 到 party – 1 范围内的整数,每个线程都不同。 这可以用来选择一个线程来做一些特殊的内务处理,例如:
i = barrier.wait() if i == 0: # Only one thread needs to print this print("passed the barrier")
如果向构造函数提供了 action,其中一个线程将在释放之前调用它。 如果此调用引发错误,则屏障将进入损坏状态。
如果调用超时,屏障将进入破坏状态。
如果在线程等待时屏障被破坏或重置,此方法可能会引发 BrokenBarrierError 异常。
- reset()
将障碍返回到默认的空状态。 任何等待它的线程都会收到 BrokenBarrierError 异常。
请注意,如果存在状态未知的其他线程,则使用此函数可能需要一些外部同步。 如果障碍被打破,最好离开它并创建一个新的障碍。
- abort()
将屏障置于破碎状态。 这会导致对 wait() 的任何活动或未来调用失败,并显示 BrokenBarrierError。 例如,如果其中一个线程需要中止,请使用此选项,以避免死锁应用程序。
最好使用合理的 timeout 值简单地创建屏障,以自动防止其中一个线程出错。
- parties
通过屏障所需的线程数。
- n_waiting
当前在屏障中等待的线程数。
- broken
如果障碍处于损坏状态,则为
True
的布尔值。
- exception threading.BrokenBarrierError
- 这个异常是 RuntimeError 的子类,当 Barrier 对象被重置或损坏时会引发。
在 with 语句中使用锁、条件和信号量
该模块提供的所有具有 acquire()
和 release()
方法的对象都可以用作 with 语句的上下文管理器。 acquire()
方法会在进入block时调用,release()
方法会在block退出时调用。 因此,以下代码段:
with some_lock:
# do something...
相当于:
some_lock.acquire()
try:
# do something...
finally:
some_lock.release()
目前,Lock、RLock、Condition、Semaphore和BoundedSemaphore对象可以用作 with 语句上下文管理器。