Semaphore(信号量)
信号量是多把锁,同时允许多个线程来更改数据,而互斥锁同时只允许一个线程更改数据。
Python信号量与互斥锁的关系
信号量的一个特殊用法是互斥量。互斥量是初始值为 1 的信号量,可以实现数据、资源的互斥访问。
Python信号量使用语法:
import threading
sem = threading.Semaphore(3)
sem.acquire()
sem.release()
Python多线程信号量使用
使用 threading.Semaphore 信号量控制多线程
import threading
import time
def run(n):
semaphore.acquire()
time.sleep(1)
print("thread:%s is running" % n)
semaphore.release()
if __name__ == '__main__':
print("111")
semaphore = threading.Semaphore(3) #最多允许3个线程同时运行
for i in range(3):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass
else:
print('----all threads done---')
条件变量
Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。线程首先acquire一个条件变量,然后判断一些条件。如果条件不满足则wait;如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。可以认为Condition对象维护了一个锁(Lock/RLock)和一个waiting池。线程通过acquire获得Condition对象,当调用wait方法时,线程会释放Condition内部的锁并进入blocked状态,同时在waiting池中记录这个线程。当调用notify方法时,Condition对象会从waiting池中挑选一个线程,通知其调用acquire方法尝试取到锁。
除了notify方法外,Condition对象还提供了notifyAll方法,可以通知waiting池中的所有线程尝试acquire内部锁。由于上述机制,处于waiting状态的线程只能通过notify方法唤醒,所以notifyAll的作用在于防止有线程永远处于沉默状态。
演示条件变量同步的经典问题是生产者与消费者问题:假设有一群生产者(Producer)和一群消费者(Consumer)通过一个市场来交互产品。生产者的”策略“是如果市场上剩余的产品少于1000个,那么就生产100个产品放到市场上;而消费者的”策略“是如果市场上剩余产品的数量多余100个,那么就消费3个产品。用Condition解决生产者与消费者问题
的代码如下:
import threading
import time
class Producer(threading.Thread):
def run(self):
global count
while True:
if con.acquire():
if count > 1000:
con.wait()
else:
count = count+100
msg = self.name+' produce 100, count=' + str(count)
print msg
con.notify()
con.release()
time.sleep(1)
class Consumer(threading.Thread):
def run(self):
global count
while True:
if con.acquire():
if count < 100:
con.wait()
else:
count = count-3
msg = self.name+' consume 3, count='+str(count)
print msg
con.notify()
con.release()
time.sleep(1)
count = 500
con = threading.Condition()
def test():
for i in range(2):
p = Producer()
p.start()
for i in range(5):
c = Consumer()
c.start()
if __name__ == '__main__':
test()
Python之Event事件
同进程的一样,线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行。
Event几种方法:
event.isSet(): 返回event的状态值;
event.wait(): 如果event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度;
event.clear(): 恢复event的状态值为False.
举例:
from threading import Thread,Event
import time
event=Event()
def light():
print('红灯正亮着')
time.sleep(3)
event.set() #绿灯亮
def car(name):
print('车%s正在等绿灯' %name)
event.wait() #等灯绿 此时event为False,直到event.set()将其值设置为True,才会继续运行.
print('车%s通行' %name)
if __name__ == '__main__':
# 红绿灯
t1=Thread(target=light)
t1.start()
# 车
for i in range(10):
t=Thread(target=car,args=(i,))
t.start()
任务示例
第一个线程中获取当前时间,判断当前时间3秒之后,触发“事件” 对象。在另一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打印。
import threading # 导入线程类
import time # 导入time类
import datetime # 导入datetime类
class eventthread(threading.Thread): # 定义事件监听类继承于Thread类
def __init__(self): # 定义构造方法
super(eventthread, self).__init__() # 重写Thread类的构造方法
def run(self): # 重写Thread类的run方法
for i in range(3): # for语句循环3秒
time1 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(time1) # 指定格式输出显示当前的时间
time.sleep(1) # 暂停1秒
event.set() # 设置事件响应
class mythread(threading.Thread): # 继承于Thread类
def __init__(self): # 定义构造方法
super(mythread, self).__init__() # 重写Thread类的构造方法
def run(self): # 重写run方法
while True: # 循环判断事件是否响应
print("考试中") # 若事件未响应输出“考试中”的信息
time.sleep(1) # 暂停1秒
if event.is_set():
print("考试结束!") # 若事件响应则输出“考试结束”的信息
break # 跳出循环
if __name__ == '__main__': # 创建主线程
event = threading.Event() # 初始化对象
et = eventthread() # 初始化一个线程对象
mt = mythread() # 初始化一个线程对象
et.start() # 启动线程
mt.start() # 启动线程