Python3之并发(五)---线程条件(Condition)和事件(Event)

摘要:
余额不足!
一、线程条件Condition(条件变量)

依赖锁对象(Lock,RLock),锁对象可以通过参数传入获得,或者使用自动创建的默认锁对象
当多个条件变量需要共享同一个锁时,建议传入锁对象

除了带有获取到锁的锁定池,Condition还包含一个未获取到锁的等待池,等待池中的线程处于等待阻塞状态,直到锁定池中的线程调用notify()/notifyAll()通知,得到通知后线程进入锁定池等待锁定

threading.Condition(lock=None)
条件变量对象,允许一个或多个线程等待,直到被另一个线程通知为止
lock: 使用的锁对象,默认None,使用RLock锁对象,其他值使用RLock或Lock锁对象

Condition对象的方法

Condition.acquire(*args)
加锁
获取锁,获取成功返回 True,否则返回 False

Condition.release()
释放锁,没有返回值

Condition.wait(timeout=None)
等待通知的最大超时时间,线程进入等待池并释放锁,直到锁定池中的其他线程调用notify()/notifyAll()通知唤醒该线程
timeout时间内获取到通知返回 True,否则返回 False
timeut: 超时时间,浮点数,默认None
当没有获去到锁的线程调用该方法引发 RuntimeError 异常

Condition.notify(n=1)
唤醒等待池中的线程(默认n=1),收到通知的线程将自动调用acquire()方法尝试加锁,若等待池中有多个线程,则随机选择当中任意一个线程

Condition.notify_all()
唤醒等待池中的所有线程

示例

食物添加和消费

import threading

class Food():
    def __init__(self, Max):
        #初始食物个数和要加入食物的最大个数
        self.num = 0
        self.max = Max

        '''
        是否加入食物的标志
        self._flag=True,表示需要加入食物,消费食物方法被堵塞
        self._flag=False,表示消费食物,加入食物方法被堵塞
        '''
        self._flag = True
        self.cond = threading.Condition()

    #加入食物方法
    def food_producer(self):
        with self.cond:
            if self._flag:
                for i in range(self.max):
                    self.num += 1
                    print(threading.current_thread().name+'线程已添加食物个数:',str(self.num),', 总共',str(self.num),'')
                print('当前食物个数:'+str(self.max),'	请消费食物')
                self._flag = False
                self.cond.notify_all()
            else:
                self.cond.wait()

    #消费食物方法
    def food_consumer(self):
        with self.cond:
            if  self._flag:
                self.cond.wait()
            else:
                for i in range(self.max):
                    self.num -= 1
                    print(threading.current_thread().name+'线程已消费食物个数:',str(i+1),',当前还剩食物个数:',str(self.num))
                print('当前食物个数:'+str(self.num)+'	食物已消费完,请添加食物')
                self._flag = True
                self.cond.notify_all()

foo = Food(20)
threading.Thread(target=foo.food_producer, name='food_producer_thread').start()
threading.Thread(target=foo.food_consumer, name='food_consumer_thread').start()

存取钱

import threading

#账户类
class Account:
    def __init__(self, account_no, balance):
        #账户编号和账户余额
        self.account_no = account_no
        self.balance = balance
        
        '''
        存取钱标志
        self._flag=True表示取钱,存钱方法被堵塞
        self._flag=False表示存钱,取钱方法被堵塞
        '''
        self._flag = False
        self.cond = threading.Condition()

    def getBlance(self):
        return self.balance
    
    #提取现金方法
    def draw(self, draw_amount):
        with self.cond:
            if not self._flag:
                self.cond.wait()
            else:
                if self.balance >= draw_amount :
                    print(threading.current_thread().name+'	取钱成功!吐出钞票:'+str(draw_amount))
                    self.balance -= draw_amount
                    print(threading.current_thread().name+'操作之后	余额为:'+str(self.balance))
                else:
                    print(threading.current_thread().name+'	取钱失败!余额不足!	当前余额为:'+str(self.balance))
                self._flag = False
                self.cond.notify_all()
 
    #存钱方法
    def deposit(self,deposit_amount):
        with self.cond:
            if  self._flag:
                self.cond.wait()
            else:
                print(threading.current_thread().name+'	存钱成功!存入钞票:'+str(deposit_amount))
                self.balance += deposit_amount
                print(threading.current_thread().name+'操作之后	余额为:'+str(self.balance))
                self._flag = True
                self.cond.notify_all()


acct = Account('987456',0)

for i in range(5):
    thread1 = threading.Thread(name="acct_deposit_thread", target=acct.deposit, args=(800,))
    thread1.start()
    thread2 = threading.Thread(name="acct_draw_thread", target=acct.draw, args=(900,))
    thread2.start()
二、线程事件Event

Event是最简单的线程通信机制之一: 一个线程发出事件信号,其他线程等待接收该信号

 不支持with 上下文

threading.Event
相当于Condition+用户自定义的Flag
Event管理一个内部标识(Flag),默认值是 False,调用 set() 方法可将Flag设置为 True ,也可以调用 clear() 方法可将Flag设置为False
当Flag=False时,调用 wait() 方法将进入阻塞状态,直到Flag为 true

Event对象的方法

Event.is_set()
Event内部标识为True返回 True

Event.set()
将Event内部标识设为 True ,所有正在等待这个事件的线程将被唤醒
当标识为 True 时,调用 wait() 方法的线程不会被被阻塞

Event.clear()
将Event内部标识设为 False ,之后调用 wait() 方法的线程将会被阻塞,直到调用 set() 方法将内部标识再次设置为 True

Event.wait(timeout=None)
线程阻塞的最大超时时间,此时间内线程一直被堵塞,直到内部标致为 True
timeout: 超时时间,默认None,浮点数,单位秒
调用该方法之前先释放锁,即调用release()释放锁,否则容易产生死锁

示例

食物添加和消费

import threading

class Food():
    def __init__(self, Max):
        #初始食物个数和要加入食物的最大个数
        self.num = 0
        self.max = Max

        self.lock = threading.RLock()
        self.event = threading.Event()

    #加入食物方法
    def food_producer(self):
        self.lock.acquire()
        try:
            if not self.event.is_set():
                for i in range(self.max):
                    self.num += 1
                    print(threading.current_thread().name+'已添加食物个数:',str(self.num)+',当前总共',str(self.num),'') 
                print('当前食物个数:'+str(self.max),'	请先消费食物')
                self.event.set()
                self.lock.release()
            else:
                self.lock.release()
                self.event.wait()
        finally:
            pass

    #消费食物方法
    def food_consumer(self):
        self.lock.acquire()
        try:
            if self.event.is_set():
                for i in range(self.max):
                    self.num -= 1
                    print(threading.current_thread().name+'已消费食物个数:',str(i+1),'个食物,当前还剩余食物个数:',str(self.num))
                print('当前食物个数:'+str(self.num)+'	食物已消费完,请添加食物')
                self.event.clear()
                self.lock.release()
            else:
                print(threading.current_thread().name+'的内置标识为{}'.format(self.event.is_set()))
                self.lock.release()
                self.event.wait()
        finally:
            pass

foo = Food(20)

for i in range(2):
    thread2 = threading.Thread(target=foo.food_consumer, name='消费者')
    thread2.start()
    thread1 = threading.Thread(target=foo.food_producer, name='生产者')
    thread1.start()

存取钱

import threading

#账户类
class Account:
    def __init__(self,account_no,balance):
        #账户编号和账户余额
        self.account_no = account_no
        self.balance = balance

        self.lock = threading.RLock()
        self.event = threading.Event()

    def getBlance(self):
        return self.balance
    
    #提取现金方法
    def draw(self,draw_amount):
        self.lock.acquire()
        try:
            if not self.event.is_set():
                print(threading.current_thread().name+'的内置标识为{}'.format(self.event.is_set()))
                self.lock.release()
                self.event.wait()
            else:
                if self.balance >= draw_amount:
                    print(threading.current_thread().name+'	取钱成功!吐出钞票:'+str(draw_amount))
                    self.balance -= draw_amount
                    print(threading.current_thread().name+'操作之后	余额为:'+str(self.balance))
                else:
                    print(threading.current_thread().name+'	取钱失败!余额不足!	当前余额为:'+str(self.balance))
                self.event.clear()
                self.lock.release()
        finally:
            pass
    
    def deposit(self,deposit_amount):
        self.lock.acquire()
        try:
            if self.event.is_set():
                self.lock.release()
                self.event.wait()
            else:
                print(threading.current_thread().name+'	存钱成功!存入钞票:'+str(deposit_amount))
                self.balance += deposit_amount
                print(threading.current_thread().name+'操作之后	余额为:'+str(self.balance))
                self.event.set()
                self.lock.release()
        finally:
            pass


acct = Account('987456', 0)

#循环取存操作5次,
for i in range(5):
    thread2 = threading.Thread(name="取钱者", target=acct.draw, args=(700,))
    thread2.start()
    thread1 = threading.Thread(name="存钱者", target=acct.deposit, args=(800,))
    thread1.start()

免责声明:文章转载自《Python3之并发(五)---线程条件(Condition)和事件(Event)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇lambda表达式封装对数据库的查询jmeter数据驱动csv+批量导出数据到csv文件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

android中Handle类的用法

android中Handle类的用法     当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉。Handler就是把这些功能放到一个单独的线程里执行,与Activity互不影响。     当用户点击一个按钮时如果...

用windbg分析一个dead lock的问题

难得Winform项目中碰到dead lock,记录一下。 QA报告说,有时候晚上跑完自动化脚本,第二天早上来发现系统hang在屏保界面没反应,从日志看也没有报错。这种属于很少才会发生,也不知道怎么重现,但是很严重的bug,于是抓个dump来研究一下。 # Windbg加载dump文件后的一些文件信息 Microsoft (R) Windows Debu...

【转】Python中的GIL、多进程和多线程

转自:http://lesliezhu.github.io/public/2015-04-20-python-multi-process-thread.html 目录 1. GIL(Global Interpretor Lock,全局解释器锁) 2. threading 2.1. 创建线程 2.2. 使用线程队列 3. dummy_threadi...

多线程中,ResultSet为空,报错空指针

最近在数据库查询数据时,由于数据量太大,使用了多线程,通过线程池建了好几个线程,然后调用了一个封装好的jdbc查询语句。 结果在多线程中,ResultSet报错空指针。 仔细查阅后,才发现多个线程访问了同一个connection,事务混乱,导致了空指针。 解决方法: 使用数据库连接池,这样一个线程各自使用一个connection,就不会有冲突了。...

利用C#线程窗口调试多线程程序

       从网上的资料判断,调试多线程程序似乎就一下3种方法。 1、在日志的某个地方写日志文件。 优点:不会干扰程序的执行,特别是对网络的多线程通信。 缺点:每次都需要打开日志文件以查看进程运行的信息。 2、利用断点进行调试。 优点:直观,可以直接看到运行过程的值 缺点:在多个线程设置断点,可能让程序跳来跳去,还需要额外地分出一部分精力用来理清程序...

SmartThreadPool

首先是实例化的时候的参数的解释 //Initialize SmartThreadPool & Make logs //SmartThreadPool m_hThreadPool; //m_hThreadPool = new SmartThreadPool();//声明一个线程池 STPStartInfo stp = new STPStartI...