赞
踩
threading模块提供Thread类和各种同步原语,用于编写多线程的程序。
Thread类用于表示单独的控制线程。使用下面的函数可以创建一个新的线程。
Thread(group=None, target=None, name=None, args=(), kwargs={})
此函数创建一个新的Thread实例。
Thread的实例t支持以下方法和属性。
下面这个例子说明如何以线程的形式创建和启动一个函数。
import threading
import time
def clock(interval):
while True:
print("the time is %s" % time.ctime())
time.sleep(interval)
t = threading.Thread(target=clock,args=(5, ))
t.daemon = True
t.start()
下面这个例子说明如何将同一个线程定义为一个类:
class ClockThread(threading.Thread):
def __init__(self, interval):
threading.Thread.__init__(self)
self.daemon = True
self.interval = interval
def run(self):
while True:
print("the time is %s" % time.ctime())
time.sleep(self.interval)
t = ClockThread(5)
t.start()
如果将线程定义为类,并且定义自己的__init__()方法,必须像上面这样调用基类构造函数Thread.init(), 如果忘记这一点,将导致严重错误。除了run()方法外,改写线程已经定义好的其他方法也会出现错误。
以上例子中,对于daemon属性的设置,是永远在后台运行的线程的常见功能。通常python在解释器退出之前,会等待所有线程终止。设置daemon标志会使解释器在主程序退出后立即退出。在这种情况下,daemonic线程将被销毁。
Timer对象用于在稍后的某个时间执行一个函数。
Timer(interval, func [, args [, kwargs]])
创建定时器对象,在过去interval秒时间之后运行函数func。 args和kwargs是传递给func的参数和关键字参数。在调用start()方法后悔启动定时器。
Timer对象实例t具有以下方法:
原语锁定(或互斥锁定)是一个同步原语,状态是“已锁定”或“未锁定”之一。两个方法acquire()和release()用于修改锁定的状态。如果状态为锁定,尝试获取锁定就被阻塞,直到锁定被释放为止。如果有多个线程等待获取锁定,当锁定被释放时,只有一个线程能够得到它。等待线程获得锁定的顺序是未定义的。
使用下面的构造函数创建新的Lock对象, 初始状态为“未锁定”
Lock()
Lock对象的实例lock支持以下方法:
可重入锁定是一个类似于Lock对象的同步原语,但同一个线程可以多次获取它。这允许拥有锁定的线程执行嵌套的acquire()和release()操作。在这种情况下,只有最外面的release()操作才能将锁定重置为未锁定状态.
使用下面的构造函数可以创建一个新的RLock对象
RLock()
RLock的实例rlock支持以下方法:
信号量是一个机遇计数器的同步原语,每次调用acquire()方法时此计数器的值减1, 每次调用release()时此计数器的值加1.如果计数器为0,acquire()方法将会阻塞,知道其他线程调用release()方法为止。
Semaphore([value])
创建一个新的信号量。value是计数器的初始值。如果省略此参数,计数器将被置为1.
Semaphore的实例s支持以下方法:
BoundedSemaphore([value])
创建一个新的信号机。value是计数器的初始值。如果省略此参数,计数器的值将被置为1.
BoundedSemaphore的工作方式和Semaphore完全相同,但release()操作的次数不能超过acquire()操作的次数。
信号机和互斥锁之间的微妙差别在于:信号机可用于发射信号。例如,可以从不同线程调用acquire()和release()方法,以便在生产者和消费者线程之间进行通信。
事件用于在线程之间通信。一个线程发出“事件”信号,一个或多个其他线程等待它。Event实例管理着一个内部标志,可以使用set()方法将它置为True, 或者使用clear()方法将它重置为false。wait()方法将阻塞直到标志位true。
Event()
创建新的Event实例,并将内部标志置为false。Event实例e具有以下方法:
尽管Event对象可用于给其他线程发信号,但不应该使用它们来实现在生产者/消费者问题中十分典型的通知。例如,应该避免写出下面这样的代码:
evt = Event()
def producer():
while True:
# 生产项目
...
evt.signal()
def consumer():
while True:
# 等待一个项目
evt.wait()
# 使用项目
...
# 清除事件并再次等待
evt.clear()
这段代码并不可靠,因为在evt.wait()和evt.clear()之间,生产者可能生产了一个新的项目。但是通过清除事件,在生产者创建一个新项目之前,使用者可能看不到这个新的项目。最好的情况是,程序将经过一段很短的停滞,对项目的处理被莫名其妙推迟了。最坏的情况是,由于事件信号丢失,整个程序将会挂起。要解决这类问题,最好使用条件变量。
条件变量是构建在另一个锁定上的同步原语。但需要线程关注特定的状态变化或事件的发生时将使用这个锁定。典型的用法是生产者/消费者问题,其中一个线程生产的数据供另一个线程使用。
使用下面的构造函数可以创建一个新的Condition实例:
Condition([lock])
创建新的条件变量。lock是可选的Lock或者RLock实例。如果未提供Lock参数,就会创建新的RLock实例供条件变量使用。
条件变量cv支持以下方法:
下面这个例子提供了使用条件变量的模板:
cv = threading.Condition()
def producer():
while True:
cv.acquire()
produce_item()
cv.notify()
cv.release()
def consumer():
while True:
cv.acquire()
while not item_is_available():
cv.wait() # 等待项目出现
cv.release()
consume_item()
使用条件变量时需要注意的是,如果存在多个线程等待同一个条件,notify()操作可能唤醒它们中的一个或多个。因此,始终有这样的可能:某个线程被唤醒后,却发现它等待的条件不存在了。这解释了在consumer()函数中使用while循环的原因。如果线程醒来,但是生产的项目已经消失,它就会回去等待下一个信号。
使用诸如Lock、RLock或Semaphore之类的锁定原语时,必须多加小心。锁定的错误管理经常导致死锁或竞争条件。依赖锁定的代码应该保证当出现异常时正确地释放锁定。典型的代码如下:
try:
lock.acquire()
# 关键部分
statements
...
finally:
lock.release()
另外,所有锁定还支持上下文管理协议(一个小小的清理器):
with lock:
# 关键部分
statements
...
在上面例子中,with语句自动获取锁定,并且在控制流离开上下文时自动释放锁定。
此外,编写代码时一般应该避免同时获取多个锁定,例如:
with lock_A:
# 关键部分
statements
...
with lock_B:
# B的关键部分
statements
...
这种写法通常很容易导致应用程序神秘死锁。尽管有几种策略可以避免出现这种情况(例如分层锁定),但最好在编写代码时就避免这种写法。
线程没有任何方法可用于强制终止或挂起。这是设计上的原因,因为编写线程程序本身就十分复杂。例如,如果某个线程已经获取了锁定,在它能够释放锁定之前强制终止或挂起它,将导致整个应用程序出现死锁。此外,终止时一般不能简单地“释放锁定”, 因为复杂的线程同步经常涉及锁定和解除锁定操作,而这些操作在执行时的次序是要十分精确的。
如果腰围终止或挂起提供支持,需要自己构建这些功能。一般的做法是在循环中运行线程,这个循环的作用是定期检查线程状态以决定它是否应该终止
python解释器被一个锁定保护,该锁定只允许一次执行一个线程,即便存在多个可用的处理器。
在计算密集型程序中,这严重限制了线程的作用, 事实上,在计算密集型应用程序中使用线程,经常比仅仅按照顺序执行同样的工作慢的多。因此,实际上应该只在主要关注I/O的程序,如网络服务器中使用线程。对于计算密度更高的任务,最好使用C扩展模块或multiprocessing模块来代替。C扩展具有释放解释器锁定和并行运行的选项,可以做到当释放锁定时不与解释器进行交互。multiprocessing模块将工作分派给不受锁定限制的单独子进程。
尽管在python中可以使用各种锁定和同步原语的组合编写非常传统的多线程程序,但有一种首推的编程方式要优于其他所有编程方法,即将多线程程序组织为多个独立任务的集合,在这些任务之间通过消息队列进行通信。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。