[b0034] python 归纳 (十九)_线程同步_条件变量

 代码:

# -*- coding: utf-8 -*-
"""
学习线程同步,使用条件变量
逻辑:
     生产消费者模型
     一个有3个大小的产品库,一个生产者负责生产,一个消费者负责消费
     每次生产或消费一个产品,产品库满了,生产者必须等待,产品库空了,消费者必须等待
     生产者的速度是消费者的速度2倍,先启动消费者,一段时间后启动生产者

总结:
    1. 条件变量底层用到R锁,对于已经获得锁的线程可以执行多次 acquire(),锁多次
    2. 不确定是否和java的这一套 ,原理一样

使用:
    1. 创建条件对象  cond = threading.Condition()
    2. 在子线程调用 申请获取锁  cond.acquire()
    3. 在程序中如果有必要 使用 cond.wait()  cond.notify() cond.notifyall()
    4. 使用完后 释放锁 cond.release()

参考:

"""
import threading
import time

class Goods:
    """ 记录产品数,最多3个产品
        被多线程操作
    """
    def __init__(self):
        self.count = 0

    def add(self,num = 1):
        self.count += num

    def sub(self):
        if self.count>=0:
            self.count -= 1

    def empty(self):
        return self.count <= 0

    def full(self):
        return self.count >= 3


class Producer(threading.Thread):
    """ 生产者
    """
    def __init__(self,condition,goods,name, sleeptime=1):
        threading.Thread.__init__(self)
        self.cond = condition
        self.goods = goods
        self.sleeptime = sleeptime
        self.name = name

    def run(self):
        cond = self.cond
        goods = self.goods

        while True:
            print "p1"
            cond.acquire() # 获取锁
            print "p2"

            while goods.full(): # 如果产品满了
                print "p3"
                cond.wait()
                print  cond._Condition__lock
                print "p4"

            print "p5"
            goods.add()
            print("num:",goods.count,"producer")

            cond.notifyAll()  # 唤醒所有等待的线程,并没有交出锁
            print  cond._Condition__lock # 锁还在
            print "p6"

            cond.release()    # 解锁资源
            print  cond._Condition__lock   # 锁不在了
            print "p7"

            time.sleep(self.sleeptime)


class Consumer(threading.Thread):
    """ 消费者
    """
    def __init__(self,condition,goods,name, sleeptime=2):
        threading.Thread.__init__(self)
        self.cond = condition
        self.goods = goods
        self.sleeptime = sleeptime
        self.name = name

    def run(self):
        cond = self.cond
        goods = self.goods

        while True:
            print "c1"
            cond.acquire() # 获取锁
            print "c2"

            while goods.empty(): # 如果没有产品了
                print "c3"
                cond.wait()   #  执行它,交出锁,进入等待池,然后阻塞,直到被唤醒
                print  cond._Condition__lock    #  执行到这里,继续获得了锁
                print "c4"

            print "c5"
            goods.sub()
            print("num:",goods.count, "consumer")

            cond.notifyAll()  # 唤醒所有等待的线程,并没有交出锁
            print  cond._Condition__lock # 锁还在
            print "c6"

            cond.release()    #  解锁资源
            print  cond._Condition__lock   # 锁不在了
            print "c7"

            time.sleep(self.sleeptime)


if __name__ == '__main__':
    g = Goods() # 共享数据
    cond = threading.Condition() # 条件判断

    # 启动消费者
    consumer = Consumer(cond,g, name="consumer")
    consumer.start()

    # 2秒后启动生产者
    time.sleep(2)

    print "m1"

    # 启动生产者
    producer = Producer(cond,g, name="producer")
    producer.start()

    print "m2"

 

2 输出:

D:\Programs\Anaconda\python.exe D:/1_practice/python/projects/downloads_modify/归类/并发/thread_sync_5.py
c1
c2
c3
m1
p1m2

p2
p5
('num:', 1, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0><_RLock owner='consumer' count=1>

p7c4

c5
('num:', 0, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0>
c7
p1
p2
p5
('num:', 1, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p5
('num:', 2, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
c1
c2
c5
('num:', 1, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0>
c7
p1
p2
p5
('num:', 2, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0>
c7
p1
p2
p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0><_RLock owner='producer' count=1>

c7p4

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0><_RLock owner='producer' count=1>

c7p4

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner='producer' count=1><_RLock owner=None count=1>

p4c7

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0><_RLock owner='producer' count=1>

c7p4

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0><_RLock owner='producer' count=1>

c7p4

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0>
 c7<_RLock owner='producer' count=1>

p4
p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7
p1
p2
p3
c1
c2
c5
('num:', 2, 'consumer')
<_RLock owner='consumer' count=1>
c6
<_RLock owner=None count=0><_RLock owner='producer' count=1>

c7p4

p5
('num:', 3, 'producer')
<_RLock owner='producer' count=1>
p6
<_RLock owner=None count=0>
p7

Process finished with exit code -1
View Code

 

3 输出解读:

 

 4 相关资料

java中的notify和notifyAll有什么区别?

先说两个概念:锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中
Reference:java中的锁池和等待池

链接:https://www.zhihu.com/question/37601861/answer/145545371

然后再来说notify和notifyAll的区别

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
Reference:线程间协作:wait、notify、notifyAll

综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

有了这些理论基础,后面的notify可能会导致死锁,而notifyAll则不会的例子也就好解释了

 

个人理解:

 

posted @ 2018-09-12 00:36  sunzebo  阅读(250)  评论(0编辑  收藏  举报