专栏首页大数据入坑指南Python自学成才之路 线程间协作 lock,condition,event的使用

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

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

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

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

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版的生产者,消费者

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不会被阻塞。

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),否则会一直沉睡(阻塞)。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Executors.newSingleThreadScheduledExecutor();线程池中放入多个线程问题

    线程池executor调用scheduleWithFixedDelay方法,同时放入三个不同调度的线程。从结果中可以看出每个线程按照自己的调度互不干扰的运行。此...

    我是李超人
  • Python自学成才之路 多线程开发

    1.创建线程 Python中提供了threading模块来创建线程,创建方式有两种。

    我是李超人
  • python自学成才之路 线程间协作之Semaphore,threading.local()

    信号量 信号量用来控制线程并发数的,信号量里面维护了一个计数器,这个计数器可以理解为锁的数量,线程通过acquire方法去申请锁,每申请到一个锁,计数器就减1...

    我是李超人
  • Jenkinsfile里定义对象和函数,获取git提交人, 发送钉钉通知

    自从开始使用Jenkinsfile作为Jenkins配置后就一发不可收,因为开发者自定义CI脚本实在太方便了。

    Ryan-Miao
  • log4net配置与初始化

    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHand...

    guokun
  • 快速掌握并发编程---线程阻塞与唤醒

    synchronized 结合 java.lang.Object 对象中的wait()、notify()、notifyAll()。

    田维常
  • 爬取博客园文章

    一直想整个爬虫玩玩,之前用Java试过...的确是术业有专攻啊,Python写起爬虫来更加方便

    Noneplus
  • Java多线程学习(五)——等待通知机制

    方法wait()的作用是使当前线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程放到“预执行队列”,并在wait()所在的代码处停止执行...

    小森啦啦啦
  • 学生管理系统_排序后通过name删除列表里的字典

    skylark
  • web安全(四)给xss小白的厕所读物(有彩蛋)

    想在互联网上做个踏实的人,而不是个喷子,喜欢技术,所以会一直更,哪怕因为各种不可抗击的因素而不得不降低频率,学习是自己的事,不管在哪,不论干啥,付出总有回报,早...

    用户5878089

扫码关注云+社区

领取腾讯云代金券