当前位置:首页 >> 脚本专栏

Python线程threading模块用法详解

本文实例讲述了Python线程threading模块用法。分享给大家供大家参考,具体如下:

threading-更高级别的线程接口

源代码:Lib/threading.py
该模块在较低级别thread模块之上构建更高级别的线程接口。另请参见mutex和Queue模块。

该dummy_threading模块适用于threading因thread缺失而无法使用的情况 。

注意: 从Python 2.6开始,该模块提供 符合 PEP 8的别名和属性,以替换camelCase受Java的线程API启发的名称。此更新的API与multiprocessing模块的API兼容 。但是,没有为camelCase名称的弃用设置计划,它们在Python 2.x和3.x中仍然完全受支持。


注意 :从Python 2.5开始,几个Thread方法引发RuntimeError 而不是AssertionError错误地调用。

该模块定义了以下功能和对象:

threading.active_count()
threading.activeCount()
返回Thread当前活动的对象数。返回的计数等于返回的列表的长度enumerate()。

在2.6版中更改:添加了active_count()拼写。

threading.Condition()
返回新条件变量对象的工厂函数。条件变量允许一个或多个线程等待,直到另一个线程通知它们。

请参阅条件对象。

threading.current_thread()
threading.currentThread()
返回当前Thread对象,对应于调用者的控制线程。如果未通过threading模块创建调用者的控制 线程,则返回具有有限功能的虚拟线程对象。

在2.6版中更改:添加了current_thread()拼写。

threading.enumerate()
返回Thread当前活动的所有对象的列表。该列表包括守护线程,由其创建的虚拟线程对象 current_thread()和主线程。它排除了尚未启动的已终止线程和线程。

threading.Event()
返回新事件对象的工厂函数。事件管理一个标志,该标志可以使用该set()方法设置为true,并使用该方法重置为false clear()。该wait()方法将阻塞,直到该标志为真。

请参阅事件对象。

类threading.local
表示线程局部数据的类。线程局部数据是其值是线程特定的数据。要管理线程本地数据,只需创建一个local(或子类)实例并在其上存储属性:

mydata = threading.local()
mydata.x = 1

对于单独的线程,实例的值将不同。

有关更多详细信息和大量示例,请参阅_threading_local模块的文档字符串 。

版本2.4中的新功能。

threading.Lock()
返回新原始锁定对象的工厂函数。一旦线程获得它,后续尝试获取它就会阻塞,直到它被释放; 任何线程都可以释放它。

请参见锁定对象。

threading.RLock()
返回新的可重入锁定对象的工厂函数。必须由获取它的线程释放重入锁。一旦线程获得了可重入锁,同一个线程可以再次获取它而不会阻塞; 线程必须在每次获取它时释放一次。

请参阅RLock对象。

threading.Semaphore([ 值] )
返回新信号量对象的工厂函数。信号量管理一个计数器,表示release()呼叫数减去acquire()呼叫数 加上初始值。该acquire()方法在必要时阻止,直到它可以返回而不使计数器为负。如果没有给出,则值默认为1。

请参见信号量对象。

threading.BoundedSemaphore([ 值] )
返回新的有界信号量对象的工厂函数。有界信号量检查以确保其当前值不超过其初始值。如果确实如此,ValueError则被提出。在大多数情况下,信号量用于保护容量有限的资源。如果信号量被释放太多次,则表明存在错误。如果没有给出,则值默认为1。

类 threading.Thread
表示控制线程的类。该类可以以有限的方式安全地进行子类化。

请参见线程对象。

类 threading.Timer
在指定的时间间隔过后执行函数的线程。

见Timer对象。

threading.settrace(func )
为从threading模块启动的所有线程设置跟踪功能。在调用sys.settrace()其run()方法之前,将为每个线程 传递 func。

版本2.3中的新功能。

threading.setprofile(func )
为从threading模块启动的所有线程设置配置文件功能。在调用sys.setprofile()其run()方法之前,将为每个线程 传递 func。

版本2.3中的新功能。

threading.stack_size([ 大小] )
返回创建新线程时使用的线程堆栈大小。可选的 size参数指定用于后续创建的线程的堆栈大小,并且必须为0(使用平台或已配置的默认值)或至少为32,768(32 KiB)的正整数值。如果未指定size,则使用0。如果不支持更改线程堆栈大小,ThreadError则引发a。如果指定的堆栈大小无效,则aValueError被提升,堆栈大小未经修改。32kB是目前支持的最小堆栈大小值,以保证解释器本身有足够的堆栈空间。请注意,某些平台可能对堆栈大小的值有特定限制,例如要求最小堆栈大小> 32kB或需要以系统内存页面大小的倍数进行分配 - 应提供平台文档以获取更多信息(4kB页面是常见的;在没有更具体的信息的情况下,建议的方法是使用4096的倍数作为堆栈大小。可用性:Windows,具有POSIX线程的系统。

2.5版中的新功能。

异常threading.ThreadError
针对各种与线程相关的错误提出,如下所述。请注意,许多接口使用RuntimeError而不是ThreadError。

下面记录了对象的详细界面。

该模块的设计基于Java的线程模型。但是,在Java使锁和条件变量成为每个对象的基本行为的地方,它们是Python中的独立对象。Python的Thread类支持Java的Thread类的行为的子集; 目前,没有优先级,没有线程组,线程不能被销毁,停止,暂停,恢复或中断。Java的Thread类的静态方法在实现时会映射到模块级函数。

下面描述的所有方法都是原子执行的。

线程对象

此类表示在单独的控制线程中运行的活动。有两种方法可以指定活动:将可调用对象传递给构造函数,或者通过覆盖run()子类中的方法。不应在子类中重写其他方法(构造函数除外)。换句话说, 只 覆盖此类的init()和run()方法。

创建线程对象后,必须通过调用线程的start()方法启动其活动。这将run()在单独的控制线程中调用该方法。

一旦线程的活动开始,线程就被认为是“活着的”。当它的run()方法终止时,它会停止活动- 通常,或者通过引发未处理的异常。该is_alive()方法测试线程是否存活。

其他线程可以调用线程的join()方法。这会阻塞调用线程,直到调用其join()方法的线程终止。

线程有一个名字。名称可以传递给构造函数,并通过name属性读取或更改。

线程可以标记为“守护程序线程”。这个标志的意义在于当只剩下守护进程线程时整个Python程序退出。初始值继承自创建线程。可以通过daemon酒店设置标志。

注意:守护程序线程在关闭时突然停止。他们的资源(例如打开文件,数据库事务等)可能无法正确发布。如果您希望线程正常停止,请使它们成为非守护进程并使用合适的信令机制,例如Event。

有一个“主线程”对象; 这对应于Python程序中的初始控制线程。它不是守护程序线程。

有可能创建“虚拟线程对象”。这些是与“外部线程”相对应的线程对象,它们是在线程模块外部启动的控制线程,例如直接来自C代码。虚拟线程对象具有有限的功能; 他们总是被认为是活着的和守护的,不能被join()编辑。它们永远不会被删除,因为无法检测外来线程的终止。

class threading.Thread(group = None,target = None,name = None,args =(),kwargs = {} )
应始终使用关键字参数调用此构造函数。参数是:

小组应该None; 在实现ThreadGroup类时为将来的扩展保留 。

target是run()方法调用的可调用对象。默认为None,意味着什么都没有被调用。

name是线程名称。默认情况下,唯一名称由“Thread- N ” 形式构成,其中N是小十进制数。

args是目标调用的参数元组。默认为()。

kwargs是目标调用的关键字参数字典。默认为{}。

如果子类重写构造函数,则必须确保Thread.init()在对线程执行任何其他操作之前调用基类构造函数()。
start()
开始线程的活动。

每个线程对象最多只能调用一次。它安排run()在单独的控制线程中调用对象的方法。

此方法将RuntimeError在同一个线程对象上多次调用if。

run()
表示线程活动的方法。

您可以在子类中重写此方法。标准run() 方法调用传递给对象构造函数的可调用对象作为目标参数(如果有),分别使用args和kwargs参数中的顺序和关键字参数。

join([ 超时] )
等到线程终止。这将阻塞调用线程,直到调用其join()方法的线程终止 - 正常或通过未处理的异常 - 或直到发生可选的超时。

当超时参数存在而不存在时None,它应该是一个浮点数,指定操作的超时(以秒为单位)(或其中的分数)。由于join()总是返回None,必须调用isAlive()后join()决定超时是否发生了-如果线程还活着时,join()调用超时。

当timeout参数不存在时None,操作将阻塞,直到线程终止。

线程可以join()多次编辑。

join()提出了RuntimeError如果试图加入当前线程因为这将导致死锁。join()在线程启动之前它也是一个错误, 并且尝试这样做会引发相同的异常。

name
字符串仅用于识别目的。它没有语义。多个线程可以赋予相同的名称。初始名称由构造函数设置。

版本2.6中的新功能。

getName()
setName()
适用于2.6之前的API name。

ident
此线程的“线程标识符”或者None线程尚未启动。这是一个非零整数。看 thread.get_ident()功能。当线程退出并创建另一个线程时,可以回收线程标识符。即使在线程退出后,该标识符也可用。

版本2.6中的新功能。

is_alive()
isAlive()
返回线程是否存活。

此方法True在run()方法启动之前返回,直到run()方法终止之后。模块函数 enumerate()返回所有活动线程的列表。

在2.6版中更改:添加了is_alive()拼写。

daemon
一个布尔值,指示此线程是否为守护程序线程(True)或不是(False)。必须在start()调用之前设置,否则RuntimeError引发。它的初始值继承自创建线程; 主线程不是守护程序线程,因此在主线程中创建的所有线程都默认为daemon = False。

当没有剩下活着的非守护进程线程时,整个Python程序退出。

版本2.6中的新功能。

isDaemon(
setDaemon()
适用于2.6之前的API daemon。

锁定对象

原始锁是一种同步原语,在锁定时不属于特定线程。在Python中,它是目前可用的最低级同步原语,由thread 扩展模块直接实现。

原始锁定处于“锁定”或“解锁”两种状态之一。它是在解锁状态下创建的。它有两种基本方法,acquire()和 release()。当状态解锁时,acquire()将状态更改为锁定并立即返回。当状态被锁定时,acquire() 阻塞直到release()另一个线程中的调用将其更改为解锁,然后该acquire()调用将其重置为已锁定并返回。该 release()方法只应在锁定状态下调用; 它将状态更改为已解锁并立即返回。如果尝试释放未锁定的锁,ThreadError则会引发a。

当acquire()等待状态转为解锁时阻塞多个线程时,只有一个线程在release()呼叫重置状态解锁时继续; 哪个等待线程继续进行未定义,并且可能因实现而异。

所有方法都以原子方式执行。

Lock.acquire([ 阻止] )
获取锁定,阻止或非阻止。

当阻塞参数设置为True(默认值)时调用,阻塞直到解锁,然后将其设置为锁定并返回True。

在使用阻塞参数设置为的情况下调用时False,请勿阻止。如果一个带阻塞的调用设置为True阻塞,则False 立即返回; 否则,将锁定设置为锁定并返回True。

Lock.release()
解锁。

锁定锁定后,将其重置为解锁状态,然后返回。如果阻止任何其他线程等待锁解锁,则只允许其中一个继续执行。

在未锁定的锁上调用时,ThreadError会引发a。

没有回报价值。

RLock对象

可重入锁是同步原语,可以由同一线程多次获取。在内部,除了原始锁使用的锁定/解锁状态之外,它还使用“拥有线程”和“递归级别”的概念。在锁定状态下,某些线程拥有锁; 在解锁状态下,没有线程拥有它。

要锁定锁,线程会调用其acquire()方法; 一旦线程拥有锁,它就会返回。要解锁锁,线程会调用其 release()方法。acquire()/ release()call对可以嵌套; 只有最后一个release()(release()最外面的一对)重置锁才能解锁并允许另一个被阻塞的线程 acquire()继续进行。

RLock.acquire([ blocking = 1 ] )
获取锁定,阻止或非阻止。

在不带参数的情况下调用:如果此线程已拥有锁,则将递归级别递增1,并立即返回。否则,如果另一个线程拥有该锁,则阻塞直到锁被解锁。锁解锁后(不属于任何线程),然后获取所有权,将递归级别设置为1,然后返回。如果多个线程被阻塞等待锁解锁,则一次只能有一个线程获取锁的所有权。在这种情况下没有返回值。

在将blocking参数设置为true的情况下调用时,执行与不带参数调用时相同的操作,并返回true。

在将blocking参数设置为false的情况下调用时,请勿阻止。如果没有参数的调用会阻塞,则立即返回false; 否则,执行与不带参数调用时相同的操作,并返回true。

RLock.release()
释放锁定,递减递归级别。如果在递减之后它为零,则将锁重置为未锁定(不由任何线程拥有),并且如果阻止任何其他线程等待锁解锁,则允许其中一个继续进行。如果在递减之后递归级别仍然非零,则锁保持锁定并由调用线程拥有。

仅在调用线程拥有锁时调用此方法。RuntimeError如果在锁定解锁时调用此方法,则引发A.

没有回报价值。

条件对象

条件变量总是与某种锁相关联; 这可以传入,或者默认创建一个。(当多个条件变量必须共享同一个锁时,传入一个是有用的。)

条件变量具有acquire()与release()该调用相关联的锁的相应方法的方法。它还有一种wait() 方法notify()和notifyAll()方法。只有在调用线程获得锁定时才调用这三个,否则 RuntimeError引发a。

该wait()方法释放锁,然后阻塞,直到它被另一个线程中的相同条件变量唤醒notify()或notifyAll()调用。一旦被唤醒,它就会重新获得锁并返回。也可以指定超时。

该notify()方法唤醒等待条件变量的其中一个线程,如果有的话正在等待。该notifyAll()方法唤醒等待条件变量的所有线程。

注意:notify()和notifyAll()方法不释放锁; 这意味着被唤醒的一个或多个线程不会wait()立即从它们的调用返回,而是仅在调用notify()或notifyAll()最终放弃锁的所有权的线程时 返回 。

提示:使用条件变量的典型编程风格使用锁来同步对某些共享状态的访问; 对状态的特定变化感兴趣的线程wait()重复调用,直到它们看到所需的状态,而线程修改状态调用notify()或者 notifyAll()当它们改变状态时它可能是其中一个服务员的期望状态。例如,以下代码是具有无限缓冲区容量的通用生产者 - 消费者情况:

# Consume one item
cv.acquire()
while not an_item_is_available():
  cv.wait()
get_an_available_item()
cv.release()

# Produce one item
cv.acquire()
make_an_item_available()
cv.notify()
cv.release()

要在notify()和之间进行选择notifyAll(),请考虑一个状态更改是否只对一个或多个等待线程感兴趣。例如,在典型的生产者 - 消费者情况下,向缓冲区添加一个项目只需要唤醒一个消费者线程。

class threading.Condition([ lock ] )
如果给出了lock参数None,则它必须是一个Lock 或RLock对象,并且它被用作底层锁。否则,将RLock创建一个新对象并将其用作基础锁。

acquire(* args )
获取底层锁。此方法在底层锁上调用相应的方法; 返回值是该方法返回的任何值。

release()
释放底层锁。此方法在底层锁上调用相应的方法; 没有回报价值。

wait([ 超时] )
等到通知或直到发生超时。如果在调用此方法时调用线程尚未获取锁定,RuntimeError则引发a。

此方法释放底层锁,然后阻塞,直到它被另一个线程中的相同条件变量唤醒notify()或notifyAll()调用,或者直到发生可选超时。一旦被唤醒或超时,它就会重新获得锁定并返回。

当超时参数存在而不存在时None,它应该是一个浮点数,指定操作的超时(以秒为单位)(或其中的分数)。

当底层锁是a时RLock,它不会使用其release()方法释放,因为当递归多次获取锁时,这实际上可能无法解锁。相反,使用了RLock类的内部接口,即使多次递归获取它也能真正解锁它。然后,在重新获取锁时,使用另一个内部接口来恢复递归级别。

notify(n = 1 )
默认情况下,唤醒一个等待此条件的线程(如果有)。如果在调用此方法时调用线程尚未获取锁定, RuntimeError则引发a。

此方法最多唤醒等待条件变量的n个线程; 如果没有线程在等待,那么这是一个无操作。

如果至少有n个 线程在等待,那么当前的实现只会唤醒n 个线程。但是,依靠这种行为是不安全的。未来的优化实现有时可能会唤醒超过 n个线程。

注意:唤醒线程实际上不会从其wait() 调用返回,直到它可以重新获取锁定。由于notify()不释放锁,其调用者应该。

notify_all()
notifyAll()
唤醒等待这种情况的所有线程。这种方法就像 notify(),但唤醒所有等待的线程而不是一个。如果在调用此方法时调用线程尚未获取锁定, RuntimeError则引发a。

在2.6版中更改:添加了notify_all()拼写。

信号量对象

这是计算机科学史上最古老的同步原语之一,由早期的荷兰计算机科学家Edsger W. Dijkstra(他使用P()而V()不是acquire()和release())发明。

信号量管理一个内部计数器,该计数器按每次acquire()调用递减并按每次 调用递增release()。计数器永远不会低于零; 当acquire()发现它为零时,它会阻塞,等待其他线程调用release()。

class threading.Semaphore([ value ] )
可选参数给出内部计数器的初始值 ; 它默认为1。如果给定的值小于0,ValueError则引发。

acquire([ 阻止] )
获取信号量。

在不带参数的情况下调用:如果内部计数器在输入时大于零,则将其减1并立即返回。如果在进入时为零,则阻塞,等待其他线程调用 release()以使其大于零。这是通过适当的互锁来完成的,这样如果多个acquire()呼叫被阻止,它们 release()将完全唤醒其中一个。实现可以随机选择一个,因此不应该依赖被阻塞的线程被唤醒的顺序。在这种情况下没有返回值。

当使用blocking设置为true 调用时,执行与不带参数调用时相同的操作,并返回true。

当阻塞设置为false 时调用,请勿阻止。如果没有参数的调用会阻塞,则立即返回false; 否则,执行与不带参数调用时相同的操作,并返回true。

release()
释放信号量,将内部计数器递增1。当它在进入时为零并且另一个线程正在等待它再次大于零时,唤醒该线程。

Semaphore示例

信号量通常用于保护容量有限的资源,例如数据库服务器。在资源大小固定的任何情况下,您应该使用有界信号量。在产生任何工作线程之前,您的主线程将初始化信号量:

maxconnections = 5
...
pool_sema = BoundedSemaphore(value=maxconnections)

一旦产生,工作线程在需要连接到服务器时调用信号量的获取和释放方法:

pool_sema.acquire()
conn = connectdb()
... use connection ...
conn.close()
pool_sema.release()

有界信号量的使用减少了导致信号量被释放的编程错误超过其获取的编程错误的可能性。

事件对象

这是线程之间通信的最简单机制之一:一个线程发出事件信号,其他线程等待它。

事件对象管理一个内部标志,该标志可以使用该set()方法设置为true,并使用该 方法重置为false clear() 。该wait()方法将阻塞,直到该标志为真。

类threading.Event
内部标志最初是假的。

is_set()
isSet()
当且仅当内部标志为真时返回true。

在2.6版中更改:添加了is_set()拼写。

set()
将内部标志设置为true。等待它变为真的所有线程都被唤醒。wait()一旦标志为真,调用的线程将不会阻塞。

clear()
将内部标志重置为false。随后,线程调用 wait()将阻塞,直到set()被调用以再次将内部标志设置为true。

wait([ 超时] )
阻止,直到内部标志为真。如果输入时内部标志为真,则立即返回。否则,阻塞直到另一个线程调用 set()将标志设置为true,或者直到发生可选的超时。

当超时参数存在而不存在时None,它应该是一个浮点数,指定操作的超时(以秒为单位)(或其中的分数)。

此方法在退出时返回内部标志,因此它将始终返回, True除非给出超时并且操作超时。

在2.7版中更改:以前,该方法始终返回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 = [],kwargs = {} )
创建一个计时器,在经过间隔秒后,将使用参数args和关键字参数kwargs运行函数。

cancel()
停止计时器,取消执行计时器的操作。这只有在计时器仍处于等待阶段时才有效。

在with语句中使用锁,条件和信号量

此模块提供的具有acquire()和 release()方法的所有对象都可以用作with 语句的上下文管理器。acquire()进入块时将调用该方法,并release()在退出块时调用该方法。

目前Lock,RLock,Condition, Semaphore,和BoundedSemaphore对象可以用作 with声明上下文管理。例如:

import threading

some_rlock = threading.RLock()

with some_rlock:
  print "some_rlock is locked while this executes"

在线程代码中导入

虽然导入机制是线程安全的,但由于提供线程安全的方式存在固有限制,因此线程导入有两个主要限制:

  • 首先,除了在主模块中,导入不应该产生产生新线程然后以任何方式等待该线程的副作用。如果生成的线程直接或间接尝试导入模块,则不遵守此限制可能导致死锁。
  • 其次,所有导入尝试必须在解释器开始关闭之前完成。仅通过从通过线程模块创建的非守护程序线程执行导入,可以最容易地实现这一点。直接使用线程模块创建的守护程序线程和线程将需要一些其他形式的同步,以确保在系统关闭开始后它们不会尝试导入。不遵守此限制将导致在解释器关闭期间出现间歇性异常和崩溃(因为后期导入尝试访问不再处于有效状态的机器)。

更多关于Python相关内容感兴趣的读者可查看本站专题:《Python进程与线程操作技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》、《Python+MySQL数据库程序设计入门教程》及《Python常见数据库操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。