Python并发与并行(5)——等待与通知

可重入锁Rlock:

死锁一般有两种情况,一种是互相竞争资源,另一种是自我的锁嵌套,看如下代码:

import threading
lock = threading.Lock()

with lock:
    print "first get lock!"
    with lock:
        print "second get lock !"

运行可看到当输出'first get lock'后程序就卡住了,这正是因为上次咱们说的锁必须被释放后才能调用,那如何解决这种锁嵌套的情况呢?python中提供了threading.Rlock锁,同一线程可以重复调用同一个threading.Rlock实例的acquire()而不被阻断,将上述代码修改为

lock=threading.Rlock()

这里还有个疑问,平常生活中我编写代码很少遇到锁嵌套的情况,而且从来没用过rlock可重入锁来编写代码,如果有知道Rlock()在实际编写代码中的重要作用的请指明,也能让我学习新知识,谢谢!

Condition

另一个经常使用的锁定机制是threading.Condition. 正如其名称一样,某个线程在通过acquire获得锁定之后,若需要在特定条件符合之前等待,可以调用wait()方法,这会释放锁定供给其他线程使用,如果其他线程的运行促成了条件成立,就可以调用同一threading.Contion中的实例的notify(),通知等待条件的一个线程可以获得锁定(此时也可能有其他线程在等待),如果等待线程获取锁定,就会从上次self.wait()方法的地方继续执行。
notify()通知等待条件的一个线程,无法预期是哪一个线程会被通知,如果等待线程有多个,可以调用notify_all(),让全部的线程去争夺锁定。
注意:wait()可以使用浮点数指定超时,单位是秒,如果等待超过指定时间,就会自动尝试获取锁定并继续执行,notify()可以指定通知的线程数量,但是python官方文档上注明了这样的使用并不安全,下面看一个使用condition机制实现的经典的生产者和消费者的模型:

import threading

def producer(c):
    for i in range(20):
        c.purchase(i)

def consumer(c):
    for i in range(20):
        c.sellout()

class Clerk:
    def __init__(self):
        self.product=-1
        self.con = threading.Condition()
    def purchase(self, product):
        with self.con:
            while self.product != -1:
                self.con.wait()
            self.product=product
            print('生产力{}'.format(self.product))
            self.con.notify()
    def sellout(self):
        with self.con:
            while self.product == -1:
                self.con.wait()
            p = self.product
            self.product=-1
            print('消费了{}'.format(p))
            self.con.notify()

c = Clerk()
threading.Thread(target=producer, args=(c,)).start()
threading.Thread(target=consumer, args=(c,)).start()

上面程序的原理是:假设开始时producer()中调用了purchase(),此时不会进入while循环体,所以不会等待,设置self.product为指定数字,由于此时没有线程在等待,所以调用notify()没有作用。假设下一次还是producer调用purchase,此时Cleark实例的product不为-1,店员无法收货物了,进入while循环体,执行wait(),线程等待同时释放锁。假设现在轮到consumer调用sellout了,由于Cleark实例的product不为-1,因此不会进入while循环体,货品被买走,且product被至为1,接着调用notify()通知等待线程,上面等待的那个线程即收到响应又开始生产产品。

Queue

现实中我们编程的话遇到这种一进一出在线程之间传递数据的方式我们一般使用Python标准连接库queue.Queue,在创建时可以指定容量,这个队列提供了必要的锁定机制,可以使用put()装入数据,也可以使用get()取走数据,因此上面的范例可以修改为:

import threading,queue

def producer(clerk):
    for i in range(20):
        clerk.put(i)
        print('店员进货{}'.format(i))


def consumer(clerk):
    for i in range(20):
        p=clerk.get()
        print('消费了{}'.format(p))

clerk = queue.Queue(1)
threading.Thread(target=producer, args=(clerk,)).start()
threading.Thread(target=consumer, args=(clerk,)).start()

你可能感兴趣的:(Python并发与并行(5)——等待与通知)