Python 线程同步机制:Lock, RLock,Condition和 Semaphore

news/2024/7/8 3:43:49 标签: 线程锁, Lock, Conditon, Semaphore

1. 线程锁 Lock,得到一个锁对象 lock,可以用 lock.acquire() 将全局变量锁起来,只允许当前上锁的线程操作;再调用 lock.release() 解锁,让其他线程有机会操作这个全局变量。

from threading import Lock

total = 0
lock = Lock()

def add():
    global lock
    global total
    for i in range(100000):
        lock.acquire()    # 上锁
        total += 1
        lock.release()    # 解锁

def desc():
    global lock
    global total
    for i in range(100000):
        lock.acquire()    # 上锁
        total -= 1
        lock.release()    # 解锁

import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

2. 可重复调用的锁 RLock,RLock是一种可重入的锁,第一个锁没有释放,第二个也能获取到锁。但要注意的一点是,acquire的次数要和release的次数相等,否则引起死锁。

from threading import RLock

total = 0
lock = RLock()
def add():
    global lock
    global total
    for i in range(1000000):
        lock.acquire()
        lock.acquire()    # 可重入的锁
        total += 1
        lock.release()
        lock.release()


def desc():
    global total
    global lock
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()

import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

3. 条件变量 Condition,除了Lock带有的锁定池外,Condition 还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用 notify()/notifyAll() 通知,得到通知后线程进入锁定池等待锁定。用 Condition时要注意,.wait() 和 .notify() 方法顺序要安排好,要先 wait()再notify(),不然会导致死锁。

import threading

class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="小爱")
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print("{} : 在 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 好啊 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 君住长江尾 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 共饮长江水 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 此恨何时已 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 定不负相思意 ".format(self.name))
            self.cond.notify()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="天猫精灵")
        self.cond = cond

    def run(self):
        with self.cond:
            print("{} : 小爱同学 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我们来对古诗吧 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我住长江头 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 日日思君不见君 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 此水几时休 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 只愿君心似我心 ".format(self.name))
            self.cond.notify()
            self.cond.wait()



if __name__ == "__main__":
    from concurrent import futures
    cond = threading.Condition()
    
    # 用同一把锁传给两个不同的线程
    xiaoai = XiaoAi(cond)       
    tianmao = TianMao(cond)

    #启动顺序很重要
    #在调用with cond 之后才能调用 wait 或者 notify 方法
    #condition 有两层锁, 一把底层锁会在线程调用了 wait 方法的时候释放, 上面的锁会在每次调用 wait 的时候分配一把并放入到 cond 的等待队列中,等到 notify 方法的唤醒
    xiaoai.start()
    tianmao.start()

4. 信号量 Semaphore,semaphore是一个内置的计数器,计数器不能小于0,当计数器为0时,acquire() 将阻塞线程直到其他线程调用 release()。可以用来控制进入代码段的程序数量。比如,文件读写, 写一般只是用于一个线程写,读可以允许有多个。

import threading
import time

class HtmlSpider(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.url = url
        self.sem = sem

    def run(self):
        time.sleep(2)
        print("got html text success")
        self.sem.release()  # release 时,会将数量加回来

class UrlProducer(threading.Thread):
    def __init__(self, sem):
        super().__init__()
        self.sem = sem

    def run(self):
        for i in range(20):
            self.sem.acquire()  # 每调用一次 acquire ,会将数量减1,减到为0则进不去
            html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
            html_thread.start()

if __name__ == "__main__":
    sem = threading.Semaphore(3)    # 信号量
    url_producer = UrlProducer(sem)
    url_producer.start()

 

本文参考文章:

python中的线程之semaphore信号量

python笔记11-多线程之Condition(条件变量)

 

 

 


http://www.niftyadmin.cn/n/1524280.html

相关文章

javascript网页自动填表_学个锤子 | 从零开始填表刷票

晚上好,同学们。之前和大家一起研究了几个在线报名表平台,随之而来又有了一个问题,当我们需要填写大量数据来完成领导安排的任务时,应该怎么办呢?接下来我们以问卷星为例。教程演示用表单地址为:https://ww…

python线程池 ThreadPoolExecutor 使用详解

从 Python3.2 开始,标准库为我们提供了 concurrent.futures 模块,它提供了 ThreadPoolExecutor 和 ProcessPoolExecutor两个类,实现了对 threading 和 multiprocessing 的进一步抽象(这里主要关注线程池),不…

Python 多进程编程 ThreadPoolExecutor, multiprocessing

一、并发与并行的区别 并发:一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行。并行:并行是指任意时刻上,有多个程序同时运行在多个CPU上所有常说高并发,没有说高并行&…

autocad 如何摆正显示_月薪3W绘图老鸟良心总结!新手入门必看AutoCAD经典绘图技巧...

月薪3w绘图老鸟新手教你汇总新手入门经典技巧,15条新手入门必看技巧,带你轻松入门AutoCAD绘图,这里分享的小技巧都是绘图很常见的技巧,大家可以选择收藏起来以后都是会用的到的。一、如何减少文件大小?执行“PURGE”命…

Python 进程间通信- Queue, Pipe, Manager

回顾线程间通信(Python多线程编程之线程间通信),线程间通信用共享变量和queue.Queue。但这两者都不能用于进程间通信。不同进程之间,变量就算同名也是位于不同的内存地址上,也是不同的变量。queue.Queue 也不能用于进程…

modem开发常用的adb命令笔记

ps –A: 所有的不同process均显示出来 grep rild 搜索 rild 字符串相关内容 ps –A|grep rild 显示出 rild 进程 ps -A|grep rild radio 888 1 141276 22504 0 0 S rild radio 890 1 133988 22292 0 0 S rild kill 888 或 kill 890 就可以杀死 rild 进程 Modem 也会重…

hive 复合类型_Hive 基本语法操练(四):Hive 复合类型

hive语法中主要提供了以下复合数据类型:1)Structs: structs内部的数据可以通过DOT(.)来存取。例如,表中一列c的类型为STRUCT{a INT; b INT},我们可以通过c.a来访问域a。2)Map(K-V对):访问指定域可以通过["指定域名…

带宽与网速之间的关系

要先清楚网速量词单位之间的关系: 1Byte 8bit 1Kb 1024bits (小字b) 1KB 1024bytes(大写B) 1Mb 1024Kb 1MB 1024KB KB与kb有什么区别? 大KB,即kilo Byte,意思是一千个字节,表示的内存大小为1024*8位&#…