Python:线程同步,Event事件、Lock锁,RLock锁、Condition消费者模型

本文介绍了Python中线程同步的概念,详细讲解了Event事件、Lock锁、RLock可重入锁以及Condition在消费者模型中的应用。通过实例分析了加锁、解锁的时机,强调了正确使用锁的重要性,并探讨了非阻塞锁和Condition的高效通知机制在多线程编程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

线程同步

Event

Event练习

Lock:锁机制

加锁、解锁

锁的引用场景

非阻塞锁使用

可重入锁RLock

Condition【用于生成者,消费者模型中】

 上例中,程序本身不是线程安全的,程序逻辑有很多瑕疵,但是可以很好的帮助理解Condition的使用,和生产消费者模型

Condition总结


 

线程同步

概念:

  • 线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作
  • 不同的操作系统实现的技术有所不同,有临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、时间Even等

Event

Event时间,是线程间通信机制中最简单的实现,使用一个内部的标记Flag, 通过Flag的True或False的变化来进行操作

名称 含义
set() 标记设置为True
clear() 标记设置为False
is_set() 标记是否为True
wait(timeout = None) 设置等待标记为True的时长,None为无限等待。等到返回Ture,未等到超时返回False

需求:老板雇佣了一个工人,让他生产被子,老板一直等待这个工人,直到生产了10个杯子

from threading import Event ,Thread
import logging
import time


FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT,level=logging.INFO)

def boss(event:Event):
    logging.info("I m boss, waiting for U.")
    #等待
    event.wait()
    logging.info("Good job")


def worker(event:Event,count=10):
    logging.info("I m working for U.")
    cups = []
    while True:
        logging.info("make 1")
        time.sleep(0.5)
        cups.append(1)
        if len(cups) >=10:
            #通知
            event.set()
            break
    logging.info("I finished my job. cups={} ".format(cups))

event = Event()
w = Thread(target=worker,args=(event,))
b = Thread(target=boss,args=(event,))

b.start()
w.start()

#结果:

2021-06-05 21:10:15,208 Thread-2 15120 I m boss, waiting for U.
2021-06-05 21:10:15,209 Thread-1 49616 I m working for U.
2021-06-05 21:10:15,209 Thread-1 49616 make 1
2021-06-05 21:10:15,722 Thread-1 49616 make 1
2021-06-05 21:10:16,228 Thread-1 49616 make 1
2021-06-05 21:10:16,734 Thread-1 49616 make 1
2021-06-05 21:10:17,243 Thread-1 49616 make 1
2021-06-05 21:10:17,746 Thread-1 49616 make 1
2021-06-05 21:10:18,251 Thread-1 49616 make 1
2021-06-05 21:10:18,756 Thread-1 49616 make 1
2021-06-05 21:10:19,257 Thread-1 49616 make 1
2021-06-05 21:10:19,764 Thread-1 49616 make 1
2021-06-05 21:10:20,268 Thread-1 49616 I finished my job. cups=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 
2021-06-05 21:10:20,268 Thread-2 15120 Good job

总结:

  • 使用同一个Event对象的标记Flag
  • 谁wait就是等到Flag变为True,或等到超时返回False。不限制等待的个数

wait使用

  • Event的wait优先于time.sleep,他会更快的切换到其他的线程,提高并发效率
     
from threading import Event ,Thread
import logging
import time

logging.basicConfig(level=logging.INFO)
def do(event:Event,interval:int):
    while not event.wait(interval):         #条件中返回True或者False
        logging.info("do sth")

e = Event()
Thread(target=do,args=(e,3)).start()

e.wait(10) #也可以使用time.sleep(10)
e.set()

print("main exit")

结果:
INFO:root:do sth
INFO:root:do sth
INFO:root:do sth
main exit

Event练习

实现Timer,延时执行线程,延时计算add(x,y)

思路:Timer的构造函数中参数得有哪些?如何实现start启动一个线程执行函数?如何cancel取消待执行任务?

思路实现
 

from threading import Event,Thread
import datetime
import logging

logging.basicConfig(level=logging.INFO)
def add(x:int,y:int):
    logging.info(x + y)


class Timer:
    def __init__(self,interval,fn,*args,**kwargs):
        self.interval = interval
        self.fn = fn
        self.args =args
        self.kwargs = kwargs
        self.event = Event()

    def start(self):
        Thread(target=self._run).start()

    def cancel(self):
        self.event.set()

    def _run(self):
        start_time = datetime.datetime.now()
        logging.info("watting")

        self.event.wait(self.interval)
        logging.info(self.event.is_set())
        if not self.event.is_set():  #也可以:if not self.event.wait(self.interval)
            self.fn(*self.args,**self.kwargs)
        delta = (datetime.datetime.now() - start_time).total_seconds()
        logging.info("finished {} ".format(delta) )
        self.event.set()


t = Timer(10,add,4,50)
t.start()
e = Event()
e.wait(4)

print("Main Process")

结果:
INFO:root:watting
Main Process
INFO:root:False
INFO:root:54
INFO:root:finished 10.000212

Lock:锁机制

锁,凡是存在共享资源争抢的地方都可以使用锁,从而保证只有一个使用者可以完全按使用这个资源

名称 含义
acquire(blocking = True, timeout=-1) 默认阻塞,阻塞可以设置超时时间。非阻塞时,timeout禁止设置。成功获取锁,返回True,否者返回False
release() 释放锁。可以从任何线程调用释放。已上锁的锁,会被重置为unlock,未上锁的锁上调用,抛出RuntimeError异常

需求:订单生产1000个杯子,阻止10个人生产   【无锁版本

from threading import Event,Thread
import time
import threading
import logging

FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)

cups =[]

def worker(count =10):
    logging.info("I`m workinf for U")
    while len(cups) < count:
        time.sleep(0.00000001)
        cups.append(1)

    logging.info("I`m finished cups = {}".format(len(cups)))

for _ in range(10):
    Thread(target=worker,args=(100,)).start()

结果:
2021-06-13 10:19:56,488 Thread-7 644 I`m workinf for U
2021-06-13 10:19:56,488 Thread-8 18800 I`m workinf for U
2021-06-13 10:19:56,488 Thread-9 17828 I`m workinf for U
2021-06-13 10:19:56,488 Thread-10 20444 I`m workinf for U
2021-06-13 10:19:56,633 Thread-3 7056 I`
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值