• [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:ProgramsAnacondapython.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则不会的例子也就好解释了

    个人理解:

  • 相关阅读:
    nSStirng、NSArray、以及枚举(Method小集合)
    http://www.cnblogs.com/beniao/category/172407.html Flex & .NET
    开发帮助文档Api
    JavaScript初学者应注意的七个细节
    javascript实现可以拖动的层示例(层拖动,兼容IE/FF)
    利用IE自带的WebBrowser进行打印
    asp 循环语句
    Form.elements[i]的使用
    Web控件tbSkin主题的应用
    asp 过程总结
  • 原文地址:https://www.cnblogs.com/sunzebo/p/9631900.html
Copyright © 2020-2023  润新知