1.锁、信号量
锁,也被称为互斥体( mutex ),是共享对象,常用于发射共享状态被读取或修改的信号。不同的编程语言实现锁的方式不同,但是在 Python 中,一个进程可以调用 acquire() 方法来尝试获得锁的“所有权”,然后在使用完共享变量的时候调用 release() 释放它。当进程获得了一把锁,任何试图执行 acquire() 操作的其他进程都会自动等待到锁被释放。这样,同一时间只有一个进程可以获得一把锁。
对于一把保护一组特定的变量的锁,所有的进程都需要编程来遵循一个规则:一个进程不拥有特定的锁就不能访问相应的变量。实际上,所有进程都需要在锁的 acquire() 和 release() 语句之间“包装”自己对共享变量的操作。
我们可以把这个概念用于银行余额的例子中。该示例的临界区是从余额读取到写入的一组操作。我们看到,如果一个以上的进程同时执行这个区域,问题就会发生。为了保护临界区,我们需要使用一把锁。我们把这把锁称为 balance_lock (虽然我们可以命名为任何我们喜欢的名字)。为了锁定实际保护的部分,我们必须确保试图进入这部分时调用 acquire() 获取锁,以及之后调用 release() 释放锁,这样可以轮到别人。
信号量。信号量是用于维持有限资源访问的信号。它们和锁类似,除了它们可以允许某个限制下的多个访问。它就像电梯一样只能够容纳几个人。一旦达到了限制,想要使用资源的进程就必须等待。其它进程释放了信号量之后,它才可以获得。
例如,假设有许多进程需要读取中心数据库服务器的数据。如果过多的进程同时访问它,它就会崩溃,所以限制连接数量就是个好主意。如果数据库只能同时支持 N=2 的连接,我们就可以以初始值 N=2 来创建信号量。
2.条件变量
条件变量是表现为信号的对象,信号表示某个条件被满足。它们通常被用于协调进程,这些进程需要在继续执行之前等待一些事情的发生。需要满足一定条件的进程可以等待一个条件变量,直到其它进程修改了条件变量来告诉它们继续执行。
Python 中,任何数量的进程都可以使用 condition.wait() 方法,用信号告知它们正在等待某个条件。在调用该方法之后,它们会自动等待到其它进程调用了 condition.notify() 或 condition.notifyAll() 函数。 notify() 方法值唤醒一个进程,其它进程仍旧等待。 notifyAll() 方法唤醒所有等待中的进程。每个方法在不同情形中都很实用。
由于条件变量通常和决定条件是否为真的共享变量相联系,它们也提供了 acquire() 和 release() 方法。这些方法应该在修改可能改变条件状态的变量时使用。任何想要用信号告知条件已经改变的进程,必须首先使用 acquire() 来访问它。
3.事件
同进程一样,线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
总结一下,信号量就是控制线程并发数,条件变量就是某种标志,满足它线程才能继续运行下去,而事件就是当条件满足时会发生的事情。以下为实例
import threading
import time
class Theard1(threading.Thread):#继承线程类,创建线程1
def __init__(self,name):
super(Theard1, self).__init__()
self.name = name
def run(self):
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))#打印当前时间
event.set() # 启动事件
time.sleep(3)#休眠3秒
event.clear() # 清除事件
class Theard2(threading.Thread): #继承线程类,创建线程2
def __init__(self,name):
super(Theard2, self).__init__()
self.name = name
def run(self):
while True:
if event.is_set(): # 如果事件在执行
print('考试正在进行中')
else:
print('考试已结束')
event.wait() # 等待事件
event = threading.Event() # 创建事件
#实例化对象
r1 = Theard1("打印当前时间")
r2 = Theard2("考试")
#启动线程
r1.start()
r2.start()