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()  # 启动线程