16.2. threading高层线程接口

Source code: Lib/threading.py


本模块的高层线程接口构建在低层的thread模块上。另请参阅mutexQueue模块。

dummy_threading模块用于因为没有thread而使得threading不可以使用的情况。

从Python 2.6开始,本模块提供遵循PEP 8的别名和属性以替换从Java的线程API中借来的骆驼拼写式的名字。这个更新后的API与multiprocessing模块中的相兼容。然而,废弃骆驼拼写式的名字还没有设定日程,在Python 2.x和3.x中它们仍然完全支持。

从Python 2.5开始,几个Thread中的方法如果错误地调用将引发RuntimeError而不是AssertionError

CPython实现细节: 在CPython中,由于全局解释器锁的存在,一次只有一个线程可以执行Python代码(即使某些性能为导向的库可以克服这个限制)。如果你希望你的应用程序更好地使用多核机器的计算资源,建议你使用multiprocessing然而,如果你想并发地运行多个I/O密集的任务,threading仍然是一个合适的模型。

本模块定义了以下的函数和对象:

threading.active_count()
threading.activeCount()

返回当前存在的Thread对象的个数。返回的数目等于enumerate()返回的列表的长度。

2.6版中的变化: 添加active_count()拼写。

threading.Condition()

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

参见Condition对象

threading.current_thread()
threading.currentThread()

返回当前的Thread对象,对应于调用者控制的线程。如果调用者控制的线程不是通过threading模块创建的,则返回一个只有有限功能的虚假线程对象。

2.6版中变化:添加current_thread()拼写。

threading.enumerate()

返回当前活着的Thread对象的列表。该列表包括守护线程、由current_thread()创建的虚假线程对象和主线程。它不包括终止的线程和还没有开始的线程。

threading.Event()

一个工厂函数,返回一个新的event对象。一个event管理一个标志,该标志可以通过set()方法设置为真或通过clear()方法重新设置为假。wait()方法将阻塞直至该标志为真。

参见Event对象

class threading.local

表示thread-local数据的一个类。thread-local数据是值只特定于线程的数据。要管理thread-local数据,只需创建local(或其子类)的一个实例并在它上面存储属性:

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

该实例的值对于各自的线程将是不同的。

更多的细节和扩展示例,参见_threading_local模块的文档字符串。

2.4版中新增。

threading.Lock()

一个工厂函数,返回一个新的原锁对象。一个线程一旦获得它,接下来尝试获得它都将阻塞,直到它被释放;任何线程都可能释放它。

参见Lock 对象

threading.RLock()

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

参见RLock 对象

threading.Semaphore([value])

一个工厂函数,返回一个新的信号量对象。一个信号量管理一个计数器,它表示release()调用的数目减去acquire()调用的数目,再加上一个初始值。acquire()方法将会阻塞直到它可以在返回时计数器不是负数。如果没有给出,value默认为1。

参见Semaphore 对象

threading.BoundedSemaphore([value])

一个工厂函数,返回一个新的有界信号量对象。一个有界信号量会确保它当前的值不超过它的初始值。如果超过,则引发ValueError在大部分情况下,信号量用于守护有限容量的资源。如果信号量被释放太多次,它是一种有bug的迹象。如果没有给出,value默认为1。

class threading.Thread

表示一个控制线程的类。该类可以用一种有限的方式安全地继承。

参见Thread 对象

class threading.Timer

一个线程,它在一个指定的时间间隔之后执行一个函数。

参见Timer 对象

threading.settrace(func)

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

2.3版中新增。

threading.setprofile(func)

为所有从threading模块启动的线程设置一个profile函数。在每个线程的run()调用之前,func将传递给sys.setprofile()

2.3版中新增。

threading.stack_size([size])

返回创建新的线程时该线程使用的栈的大小。可选的size参数指定后来创建的线程使用栈的大小,它必须是0(使用平台的或者配置的默认值)或不少于32,768(32kB)的正整数。如果不支持改变线程栈的大小,则引发一个ThreadError如果指定的栈的大小不合法,则引发一个ValueError且栈的大小不会改变。32kB是目前支持的最小的栈大小值以保证解释器自己有充足的栈空间。注意,某些平台可能对栈的大小具有特别的限制,例如要求最小的栈大小>32KB或要求分配系统内存页面大小的整数倍 - 更多的信息应该参考平台的文档(通常是4KB的页面;在缺少更多特定信息的情况下栈的大小建议使用4096的整数倍)。可用的平台:Windows、 带有POSIX线程的系统。

2.5版中新增。

exception threading.ThreadError

用于各种线程相关的错误,如下文描述。注意许多接口使用RuntimeError而不用ThreadError

各个对象的详细接口的文档在下面。

本模块的设计零散地基于Java的线程模型。然而,Java的锁和条件变量是每个对象的基本行为,在Python中它们是单独的对象。Python的Thread类支持Java线程类行为的一个子集;目前,它们没有优先级,没有线程组且线程不可以销毁、挂起、恢复和中断。Java线程类的静态方法,如果实现,则映射成模块级别的函数。

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

16.2.1. Thread 对象

这个类表示在单独的一个控制线程中运行的一个活动。有两种指定活动的方法:通过传递一个可调用对象给构造函数,或在子类中覆盖run()方法。在子类中不应该覆盖其它方法(构造函数除外)。换句话说,就是覆盖该类的__init__()run()方法。

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

线程的活动一旦开始,该线程就被认为是‘活着的’。在它的run()方法终止时它停止存活 – 要么正常地,要么通过引发一个未处理的异常。is_alive()方法测试该线程是否活着。

其它线程可以调用另外一个线程的join()方法。这将阻塞调用的线程直至join()方法被调用的线程终止。

一个线程具有一个名字。这个名字可以传递给构造函数,以及通过name属性读取和更改。

一个线程可以标志为一个“后台线程”。该标志的意思是整个Python程序在只剩下后台线程时才退出。它的初始值继承自创建它的线程。该标志可以通过daemon属性设置。

守护线程在关机时被意外地终止。它们的资源(例如打开的文件、数据库事务等)可能没有正确地释放。如果你想你的线程优雅地停止,可以把他们设置为为非守护的并使用一个合适的信号机制例如一个Event

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

可能创建的是“虚拟的线程对象”。这些线程对象对应于“异质的线程”,它们是在threading模块之外启动的控制线程,例如直接从C代码中启动的线程。虚拟的线程对象具有有限的功能;它们始终被认为是活着的和守护的,且不可以被join()它们永远不会被删除,因为异质线程的终止不可能检测到。

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})

应该始终以关键字参数调用该构造函数。参数有:

group应该为None被保留用于未来实现了ThreadGroup类时的扩展。

target是将被run()方法调用的可调用对象。默认为None,表示不调用任何东西。

name是线程的名字。默认情况下,以“Thread-N”的形式构造一个唯一的名字,N是一个小的十进制整数。

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

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

如果子类覆盖该构造函数,它必须保证在对线程做任何事之前调用基类的构造函数(Thread.__init__())。

start()

开始线程的活动。

每个线程对象必须只能调用它一次。它为对象的run()方法在一个单独的控制线程中调用做准备。

在相同的线程对象上调用该方法多次将引发一个RuntimeError

run()

表示线程活动的方法。

你可以在子类中覆盖这个方法。标准的run()方法调用传递给对象构造函数target参数的可调用对象,如果存在,分别从argskwargs参数获取顺序参数和关键字参数。

join([timeout])

等待直至线程终止。它阻塞调用线程直至join()方法被调用的线程终止 – 要么正常地要么通过一个未处理的异常 – 或者直至timeout发生。

timeout参数存在且不为None时,它应该以一个浮点数指定该操作的超时时间,单位为秒(可以是小数)。由于join()永远返回None,你必须在join()之后调用isAlive()来判断超时是否发生 – 如果线程仍然活着,则join()调用超时。

如果timeout参数不存在或者为None,那么该操作将阻塞直至线程终止。

一个线程可以被join()多次。

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

name

一个字符串,只用于标识的目的。它没有语义。多个线程可以被赋予相同的名字。初始的名字通过构造函数设置。

2.6版中新增。

getName()
setName()

2.6版之前用于name的API。

ident

线程的ID,如果线程还未启动则为None它是一个非零的整数。参见thread.get_ident()函数。当一个线程退出另外一个线程创建时,线程的ID可以重用。即使在线程退出后,其ID仍然可以访问。

2.6版中新增。

is_alive()
isAlive()

返回线程是否还活着。

run()方法刚开始之前至run()方法刚终止之后,该方法返回True模块级别的函数enumerate()返回所有活着的函数的一个列表。

2.6版中变化:增加is_alive()拼写。

daemon

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

整个Python程序在没有活着的非守护进程时退出。

2.6版中新增。

isDaemon()
setDaemon()

2.6版之前用于daemon的API。

16.2.2. Lock 对象

原锁是一个同步原语,当它锁住时不归某个特定的线程所有。在Python中,它是目前可用的最底层的同步原语,直接通过thread的扩展模块实现。

一个原锁处于“locked”或者“unlocked”状态中的一种。它创建时处于unlocked状态。它有两个基本的方法,acquire()release()当状态是unlocked时,acquire()改变该状态为locked并立即返回。当状态是locked时,acquire()将阻塞直至在另外一个线程中调用release()来将它变为unlocked,然后acquire()调用将它重置为locked并返回。release()方法应该只在locked状态下调用;它改变状态为unlocked并立即返回。如果尝试释放一个unlocked的锁,将引发一个ThreadError

当多个线程由于acquire()等待状态变为unblocked时阻塞,当一个release()调用重置状态为unblocked时,只有一个线程能够继续;具体哪一个等待的线程能够继续没有定义,不同的实现可能不同。

所有方法的执行都是原子的。

Lock.acquire([blocking])

获取一把锁,阻塞的或者非阻塞的。

当调用时blocking参数设置为True(默认值),将阻塞直至锁变成unblocked,然后设置它的状态为locked并返回True

当调用时blocking参数设置为False,将不会阻塞。If a call with blocking set to True would block, return False immediately; otherwise, set the lock to locked and return True.

Lock.release()

释放一把锁。

当锁是locked时,重置它为unlocked,然后返回。如果存在其他阻塞的线程正在等待锁变成unblocked状态,只会允许它们中的一个继续。

在一把没有锁住的锁上调用时,引发一个ThreadError

没有返回值。

16.2.3. RLock 对象

一个可重入所示一个同步原语,它可以被相同的线程获得多次。在内部,除了原始锁使用的锁定/解锁状态之外,它还使用“拥有线程”和“递归级别”的概念。在锁定状态下,某些线程拥有锁;in the unlocked state, no thread owns it.

To lock the lock, a thread calls its acquire() method; this returns once the thread owns the lock. To unlock the lock, a thread calls its release() method. acquire()/release() call pairs may be nested; only the final release() (the release() of the outermost pair) resets the lock to unlocked and allows another thread blocked in acquire() to proceed.

RLock.acquire([blocking=1])

Acquire a lock, blocking or non-blocking.

When invoked without arguments: if this thread already owns the lock, increment the recursion level by one, and return immediately. Otherwise, if another thread owns the lock, block until the lock is unlocked. Once the lock is unlocked (not owned by any thread), then grab ownership, set the recursion level to one, and return. If more than one thread is blocked waiting until the lock is unlocked, only one at a time will be able to grab ownership of the lock. There is no return value in this case.

When invoked with the blocking argument set to true, do the same thing as when called without arguments, and return true.

When invoked with the blocking argument set to false, do not block. If a call without an argument would block, return false immediately; otherwise, do the same thing as when called without arguments, and return true.

RLock.release()

Release a lock, decrementing the recursion level. If after the decrement it is zero, reset the lock to unlocked (not owned by any thread), and if any other threads are blocked waiting for the lock to become unlocked, allow exactly one of them to proceed. If after the decrement the recursion level is still nonzero, the lock remains locked and owned by the calling thread.

Only call this method when the calling thread owns the lock. A RuntimeError is raised if this method is called when the lock is unlocked.

There is no return value.

16.2.4. Condition Objects

一个条件变量总是与某种锁相关联this can be passed in or one will be created by default. (Passing one in is useful when several condition variables must share the same lock.)

A condition variable has acquire() and release() methods that call the corresponding methods of the associated lock. It also has a wait() method, and notify() and notifyAll() methods. These three must only be called when the calling thread has acquired the lock, otherwise a RuntimeError is raised.

The wait() method releases the lock, and then blocks until it is awakened by a notify() or notifyAll() call for the same condition variable in another thread. Once awakened, it re-acquires the lock and returns. It is also possible to specify a timeout.

The notify() method wakes up one of the threads waiting for the condition variable, if any are waiting. The notifyAll() method wakes up all threads waiting for the condition variable.

Note: the notify() and notifyAll() methods don’t release the lock; this means that the thread or threads awakened will not return from their wait() call immediately, but only when the thread that called notify() or notifyAll() finally relinquishes ownership of the lock.

Tip: the typical programming style using condition variables uses the lock to synchronize access to some shared state; threads that are interested in a particular change of state call wait() repeatedly until they see the desired state, while threads that modify the state call notify() or notifyAll() when they change the state in such a way that it could possibly be a desired state for one of the waiters. For example, the following code is a generic producer-consumer situation with unlimited buffer capacity:

# 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()

To choose between notify() and notifyAll(), consider whether one state change can be interesting for only one or several waiting threads. E.g. in a typical producer-consumer situation, adding one item to the buffer only needs to wake up one consumer thread.

class threading.Condition([lock])

If the lock argument is given and not None, it must be a Lock or RLock object, and it is used as the underlying lock. Otherwise, a new RLock object is created and used as the underlying lock.

acquire(*args)

Acquire the underlying lock. This method calls the corresponding method on the underlying lock; the return value is whatever that method returns.

release()

Release the underlying lock. This method calls the corresponding method on the underlying lock; there is no return value.

wait([timeout])

Wait until notified or until a timeout occurs. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.

This method releases the underlying lock, and then blocks until it is awakened by a notify() or notifyAll() call for the same condition variable in another thread, or until the optional timeout occurs. Once awakened or timed out, it re-acquires the lock and returns.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof).

When the underlying lock is an RLock, it is not released using its release() method, since this may not actually unlock the lock when it was acquired multiple times recursively. Instead, an internal interface of the RLock class is used, which really unlocks it even when it has been recursively acquired several times. Another internal interface is then used to restore the recursion level when the lock is reacquired.

notify(n=1)

默认情况下,唤醒一个线程等待这个条件,如果有的话。如果调用线程在调用此方法时尚未获取锁,则会引发RuntimeError

This method wakes up at most n of the threads waiting for the condition variable; it is a no-op if no threads are waiting.

如果n线程正在等待时,当前的实现将正好唤醒n个线程。但是,依靠这种行为是不安全的。未来,优化的实现可能偶尔会从n线程唤醒。

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

notify_all()
notifyAll()

唤醒所有线程等待这种情况。该方法的作用类似于notify(),但唤醒所有等待的线程而不是一个。如果调用线程在调用此方法时尚未获取锁,则会引发RuntimeError

Changed in version 2.6: Added notify_all() spelling.

16.2.5. Semaphore Objects

This is one of the oldest synchronization primitives in the history of computer science, invented by the early Dutch computer scientist Edsger W. Dijkstra (he used P() and V() instead of acquire() and release()).

A semaphore manages an internal counter which is decremented by each acquire() call and incremented by each release() call. The counter can never go below zero; when acquire() finds that it is zero, it blocks, waiting until some other thread calls release().

class threading.Semaphore([value])

The optional argument gives the initial value for the internal counter; it defaults to 1. If the value given is less than 0, ValueError is raised.

acquire([blocking])

Acquire a semaphore.

When invoked without arguments: if the internal counter is larger than zero on entry, decrement it by one and return immediately. If it is zero on entry, block, waiting until some other thread has called release() to make it larger than zero. This is done with proper interlocking so that if multiple acquire() calls are blocked, release() will wake exactly one of them up. The implementation may pick one at random, so the order in which blocked threads are awakened should not be relied on. There is no return value in this case.

When invoked with blocking set to true, do the same thing as when called without arguments, and return true.

When invoked with blocking set to false, do not block. If a call without an argument would block, return false immediately; otherwise, do the same thing as when called without arguments, and return true.

release()

Release a semaphore, incrementing the internal counter by one. When it was zero on entry and another thread is waiting for it to become larger than zero again, wake up that thread.

16.2.5.1. Semaphore

Semaphores are often used to guard resources with limited capacity, for example, a database server. In any situation where the size of the resource is fixed, you should use a bounded semaphore. Before spawning any worker threads, your main thread would initialize the semaphore:

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

Once spawned, worker threads call the semaphore’s acquire and release methods when they need to connect to the server:

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

The use of a bounded semaphore reduces the chance that a programming error which causes the semaphore to be released more than it’s acquired will go undetected.

16.2.6. Event Objects

事件对象是线程间最简单的通信机制之一:线程可以激活在一个事件对象上等待的其他线程

每个事件对象管理一个内部标志,可以在事件对象上调用set() 方法将内部标志设为true,调用 clear() 方法将内部标志重置为false。在事件对象调用 wait() 方法时,线程将阻塞直到事件对象的内部标志被设为true。

class threading.Event

内部旗帜初始化为false

is_set()
isSet()

如果内部标志为真,则返回true。

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

set()

将内部标志设置为true。所有的线程等待它成为真正的被唤醒标记为true后调用wait()的线程根本不会被阻塞。

clear()

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

wait([timeout])

阻止内部标志为真。如果内部标志在输入时为真,立即返回。Otherwise, block until another thread calls set() to set the flag to true, or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof).

This method returns the internal flag on exit, so it will always return True except if a timeout is given and the operation times out.

Changed in version 2.7: Previously, the method always returned None.

16.2.7. Timer 对象

这个类表示一个动作应该在一个特定的时间之后运行 — 也就是一个计时器。TimerThread的子类, 因此也可以使用函数创建自定义线程

Timers通过调用它们的start()方法作为线程启动。timer可以通过调用cancel()方法(在它的动作开始之前)停止。timer在执行它的动作之前等待的时间间隔可能与用户指定的时间间隔不完全相同。

例如:

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={})

创建一个timer,在interval秒过去之后,它将以参数args和关键字参数kwargs运行function

cancel()

停止timer,并取消timer动作的执行。这只在timer仍然处于等待阶段时才工作。

16.2.8. 在with中使用locks、conditions和semaphores 

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

目前,LockRLockConditionSemaphoreBoundedSemaphore对象可以用作with语句的上下文管理器。例如:

import threading

some_rlock = threading.RLock()

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

16.2.9. 多线程代码中的导入

虽然导入机制是线程安全的,但是由于提供线程安全的方式与生俱来的限制,在线程化的导入上有两个重要的限制:

  • 首先,如果不在主模块中,导入不应该具有副作用,如生成一个新的线程然后无条件等待那个线程。如果生成的线程直接或间接地尝试导入一个模块,不遵守这个限制可能导致一个死锁。
  • 其次,所有导入的尝试必须在解释器开始关闭它自己时完成。做到这点最简单地就是只在threading模块创建的非守护线程中做导入操作。守护线程和直接用thread模块创建的线程将要求其他形式的同步来确保在系统开始关闭后它们不会尝试导入。不遵守这个限制将导致在解释器关闭过程中出现间歇性的异常和崩溃(因为延迟访问机制不再处于一个合法的状态)。