前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python自学成才之路 线程间协作 lock,condition,event的使用

Python自学成才之路 线程间协作 lock,condition,event的使用

作者头像
我是李超人
发布2020-09-08 15:05:08
9680
发布2020-09-08 15:05:08
举报
文章被收录于专栏:大数据入坑指南

多线程并发时会出现线程安全问题,如果不解决线程并发安全问题可能会让程序出现不可预料的情况。python提供了一些工具包来解决多线程安全问题,下面介绍其中常见的工具。

1.Threading.Lock() 锁的作用是将一段操作管理起来,确保每个时刻只有一个线程可以执行这段操作,这样就能确保里面的共享变量在并发的情况下对它的操作不会出现预期之外的一次。比如有多个售票窗口,如果不对售票操作加锁,就会导致出售的门票数大于可售的门票数。

lock常用方法: lock.acquire:获取锁,锁被其它线程持有时会被阻塞 lock.release:释放锁

代码语言:javascript
复制
import threading
import random

class WindowThread(threading.Thread):

    def __init__(self,name):
        threading.Thread.__init__(self,name=name)
        self.name = name
        self.tickts = 0

    def run(self):
        global tickt_count

        while tickt_count > 0:

            lock.acquire()

            print('%s:有%d张票剩余 ' %(self.name,tickt_count))

            if tickt_count > 2:
                number = random.randint(1,2)
            else:
                number = 1

            tickt_count -= number
            self.tickts += number

            print('%s 出售 %d 张票'
                  % (self.name, number))

            lock.release()

        print('%s :所有的票已经售罄'%self.name)



if __name__ == '__main__':
    lock = threading.Lock()
    tickt_count = 100

    window1 = WindowThread('window1')
    window2 = WindowThread('window2')
    window3 = WindowThread('window3')

    window1.start()
    window2.start()
    window3.start()
    window1.join()
    window2.join()
    window3.join()

输出:
window1 :有100张票剩余 
window1 出售 2 张票
...
window1 出售 2 张票
window2 出售 1 张票
window2 :有2张票剩余 
window3 :有2张票剩余 
window2 出售 1 张票
window3 出售 1 张票
window3 :所有的票已经售罄
window1 :所有的票已经售罄
window2 :所有的票已经售罄

2.Condition 条件变量允许一个或多个线程等待,直到它们被其它线程唤醒。Condition遵循上下文管理协议。

常见方法: acquire: 请求锁 release:释放锁 wait: 线程挂起,等待被唤醒(notify或notifyAll),可以设置等待超时时间 notify:唤醒等待线程,里面可以指定唤醒几个等待线程,比如设置n=3,则表示随机唤醒等待的三个线程。 notify_all: 唤醒所有的等待线程。

案例: condition版的生产者,消费者

代码语言:javascript
复制
import threading
import random
import time

gCondition = threading.Condition()
gMoney = 1000
gTimes = 0
totalTimes = 100


class Producer(threading.Thread):

    def run(self):
        global gMoney
        global gTimes
        while True:
            with gCondition:
                if gTimes <= totalTimes:
                    money = random.randint(100, 1000)
                    gMoney += money
                    print('生产者%s 生产了%s元' % (threading.current_thread(), money))
                    gTimes += 1
                else:
                    break
                # 通知等待线程
                gCondition.notify_all()
            time.sleep(0.5)


class Consumer(threading.Thread):

    def run(self):
        global gMoney
        global gTimes
        while True:
            with gCondition:
                money = random.randint(100, 1000)
                while money > gMoney:
                    if gTimes > totalTimes:
                        return
                    print('消费者想要消费%s, 余额不足%s,等待...' % (money, gMoney))
                    gCondition.wait()
                gMoney -= money
                print('消费者%s消费了%s元' % (threading.current_thread(), money))
            time.sleep(0.5)


def main():
    for x in range(3):
        t = Producer(name='producer_' + str(x))
        t.start()

    for x in range(5):
        t = Consumer(name='consumer_' + str(x))
        t.start()


if __name__ == '__main__':
    main()

condition遵循上下文管理协议,所以可以结合with来使用,对于上下文管理协议可以看我之前的文章。with condition会在进入with之前自动执行condition.acquire,离开with的时候自动执行condition.release。

3.Event 事件对象管理一个内部标志,通过set()方法将其设置为True,并使用clear()方法将其设置为False。wait()方法阻塞,直到标志为True。该标志初始为False。

常用方法: is_set: 获取内部标志状态 set: 将内部标志设置为True。所有等待的线程都被唤醒 clear:将内部标志重置为False wait:阻塞直到内部标志为true,可以设置等待超时时间。

注意:wait不会将内部标志修改为false,如果内部标志本来就为true,调用wait不会被阻塞。

代码语言:javascript
复制
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()
    print('%s通行' %name)


if __name__ == '__main__':
    # 红绿灯
    t1 = Thread(target=light)
    t1.start()
    # 车
    for i in range(3):
        t = Thread(target=car, args=('car-' + str(i),))
        t.start()
输出:
红灯等待
car-0正在等绿灯
car-1正在等绿灯
car-2正在等绿灯
car-1通行
car-2通行
car-0通行

Event和condition最大的区别在于,condition调用wait的时候肯定会被阻塞,直到另外一个线程调用notify或notifyall将其唤醒,但是event不会调用wait不见得被阻塞,只有当内部标志为false的时候,event调用wait才会被阻塞。Event就好比十字路口的交通信号灯,绿灯的时候所有车辆必须通行(也就是没法阻塞,你不走后面的车主会揍你的),红灯的时候所有车辆都得等待。而condition就好比沉睡的公主,她睡着后(wait)必须有人将她唤醒(notify/notifyall),否则会一直沉睡(阻塞)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档