【每日力扣Leetcode】1114-按序打印(多解法来深入理解python中的锁和阻塞)

PyCharm Structure窗口中的图标含义 1、在使用pycharm的Structure时可以看到有这些图标 图标上的字母是简写,具体含义如下 在pycharm中是自动补全的变量的类别 p:parameter 参数 m:method 方法 c:class 类 其他的也类似,大同小异 v:variable 变量 f:function 函数 另外,字母左上角有锁的图样子的,表示权限为私有。一般不应改动。 左上角圆圈的含义暂时不明: 2、函数和方法的区别 与类和实例无绑定关系的function都属于函数(function); 与类和实例有绑定关系的fun 阅读详情

力扣题库1114,在单例模式的前提下,使得类中的3个方法按照指定顺序执行。

题目链接点这里。

题目描述

我们提供了一个类:

public class Foo {
  public void first() { print("first"); }
  public void second() { print("second"); }
  public void third() { print("third"); }
}

三个不同的线程将会共用一个 Foo 实例。

  • 线程 A 将会调用 first() 方法
  • 线程 B 将会调用 second() 方法
  • 线程 C 将会调用 third() 方法

请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。

解题思路

看到这道题,三个线程公用一个实例,首先想到的是单例模式。于是想着在类里面设置一个变量,这里因为题目指定了三个线程公用同一个实例,所以类变量还是实例变量不重要。然后3个方法的执行对应变量的3个不同的值。但是单单有变量的判断还不够,因为子线程没有阻塞,所以3个线程同时在跑,最终永远只有first()方法被执行。

所以用如下死循环的思路加入阻塞。

变量的初始值为1,方法first()可以直接执行,执行完毕将变量值改为2;方法second()执行前先做一个死循环,退出条件是变量值为2;方法third()类似,不过变量值需要为3。

初始答案

class Foo:
    def __init__(self):
        self.temp = 1

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.temp = 2


    def second(self, printSecond: 'Callable[[], None]') -> None:
        while self.temp != 2:
            pass 
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.temp = 3


    def third(self, printThird: 'Callable[[], None]') -> None:
        while self.temp != 3:
            pass
        # printThird() outputs "third". Do not change or remove this line.
        printThird()

这样子可以达到目的,但是效率很差,执行用时2244ms,仅超过了10.42%的提交。

改进答案一

死循环无法做到有效的监控线程,所以考虑引入threading模块。

首先是利用锁来进行阻塞。

创建2个锁,分别对应着后面两个方法的运行开关

import threading

class Foo:
    def __init__(self):
        self.lock1 = threading.Lock()
        self.lock2 = threading.Lock()
        self.lock1.acquire()
        self.lock2.acquire()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.lock1.release()


    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.lock1.acquire()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.lock1.release()
        self.lock2.release()

    def third(self, printThird: 'Callable[[], None]') -> None:
        self.lock2.acquire()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()

首先定义两个锁,然后类在实例化的时候创建并获得了锁,此后针对这两个锁如果没有release()操作的话是会阻塞的。当first()方法执行完毕释放了lock1,之后second()方法才会被执行,然后释放lock2,之后third()方法才会被执行。

这样修改以后确实效率大增,执行用时56ms,超过了40%的提交。

改进答案二

前面虽然用锁解决了问题,但是加入函数多了需要用到的锁个数也会增加,这并不是个可扩展的方案。

下面尝试用threading模块中的Condition来解决。

Condition底层也是通过锁实现的,相当于在锁的基础上又加了一层判断条件。初始化的时候使用threading.Condition([lock/rlock]),如果不传递一个锁对象,会自动生成。之后可以通过Condition对象来调用下面几种方法

  • acquire

    获取锁,调用底层锁的对应方法。通常用with来代替。

  • release

    释放锁,调用底层锁的对应方法。通常用with来代替。

  • wait

    线程挂起,等待被其他线程的notify或者notify_all唤醒。该方法会释放底层锁,然后阻塞。调用该方法的线程必须先获得锁,否则引发RuntimeError报错

  • wait_for

    需要带一个可调用对象,当该对象返回True时被唤醒,通常用lambda。其余跟wait一样

  • notify

    唤醒等待池中的一个线程(如果有),调用该方法的线程必须先获得锁,否则引发RuntimeError报错。

  • notify_all

    跟notify类似,不过是唤醒所有等待池中的线程

import threading
class Foo:
    def __init__(self):
        self.con = threading.Condition()
        self.temp = 0

    def first(self, printFirst: 'Callable[[], None]') -> None:
        with self.con:
            self.con.wait_for(lambda: self.temp == 0)
            # printFirst() outputs "first". Do not change or remove this line.
            printFirst()
            self.temp = 1
            self.con.notify_all()


    def second(self, printSecond: 'Callable[[], None]') -> None:
        with self.con:
            self.con.wait_for(lambda: self.temp == 1)
            # printSecond() outputs "second". Do not change or remove this line.
            printSecond()
            self.temp = 2
            self.con.notify_all()


    def third(self, printThird: 'Callable[[], None]') -> None:
        with self.con:
            self.con.wait_for(lambda: self.temp == 2)
            # printThird() outputs "third". Do not change or remove this line.
            printThird()
            self.temp = 3
            self.con.notify_all()

这样优化以后执行用时48ms,超过了69.85%的提交。

改进答案三

阻塞还可以使用队列,分两种情况。对于不定长队列,当队列为空时,消费者那一端因为读不到内容会阻塞;对于定长队列,当队列满了时,生产者那一端因为无法写入内容会阻塞。

这里采用第一种非定长队列的方法来实现。

import queue 
class Foo:
    def __init__(self):
        self.q1 = queue.Queue()
        self.q2 = queue.Queue()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.q1.put(1)


    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.q1.get()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.q2.put(1)


    def third(self, printThird: 'Callable[[], None]') -> None:
        self.q2.get()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()

这里同样需要两个队列,最后执行用时44ms,超过83%的提交。

注意事项

因为不涉及到锁的获取和释放,队列方式还是要好一些。

但是如果说扩展性,Condition对象通过一个对象加一个变量就可以处理多个线程。同时还可以把每个方法中的重复代码单独提取为一个独立方法进行调用。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

Python基础091:解决Pycharm的console控制台命令提示符是In[2]而不是>>>的问题 | 设置console为Cpython | terminal终端 | console控制台 问题 解决Pycharm的python console控制台命令提示符是In[2]而不是>>>的问题,如下所示: 解决方法 在 Setting中 搜索 ipython ,找到Console ,单击Console,将【Use IPython if available】 的对勾去掉,然后重启Pycharm即可 结果 已经顺利变成>>>啦 ... 阅读详情

相关推荐

服务器被矿工入侵记录

一次服务器被矿工入驻的处理记录

王瑞的技术专栏 1332

Pycharm代码自动补全中的c,f,v,m,p分别是什么意思?

点号可能是程序员敲得最多的符号了,每次敲完后面都会弹出一堆自动补全的提示供你选择。这里总结一下Pycharm点号后自动补全的几种类型标识符。 我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。 如下图红框所示,每个提示前面都有一个不同颜色不同字母的标识符 这里把Pycharm官网的解释记录一下供以后查看,官网地址在这里...

T型人小付的博客 3174

抖音风控参数逆向实战:从bd-ticket-guard-client-data到x-tt-session-dtrait的完整攻防解析

本文深入解析抖音风控参数逆向实战,重点剖析bd-ticket-guard-client-data和x-tt-session-dtrait的生成机制与破解方法。通过抓包调试、ECDH密钥生成、RSA与AES双层加密等技术手段,揭示抖音风控系统的核心逻辑,并提供实用的对抗策略与维护建议,助力开发者突破抖音风控限制。

weixin_30335575的博客 674

力扣1114.按序打印-----题目解析

【代码】力扣1114.按序打印-----题目解析。

weixin_52293201的博客 849

3dsMax高效导入OSGB倾斜模型的3个关键步骤与插件应用

本文详细解析了在3dsMax中高效导入OSGB倾斜模型的三个核心步骤:建立索引、处理贴图路径以及选择合适的插件。针对导入过程中常见的模型崩溃、贴图丢失和操作卡顿问题,提供了从数据结构理解到插件选型的实战解决方案,帮助用户实现大规模实景模型在3dsMax中的流畅导入与编辑。

weixin_29238147的博客 467

HA集群 执行 hdfs haadmin -transitionToActive 失败

最近把一个单NameNode集群变更为HA集群,过程中都很顺利,nn1和nn2两个NameNode都显示standby状态,但是把nn1或者nn2切换到active状态时就会报错抛异常。 查看master上namenode启动日志,如下: Incompatible clusterID for journal Storage Directory /home/hadoop-zero/bigdat...

qq_34727158的博客 1763

pycharm提示符 f p m c v 代表含义

Python中函数(Function)和方法(Method)的区别: 与类和实例无绑定关系的function都属于函数(function); 与类和实例有绑定关系的function都属于方法(method)。

zhiboqingyun的博客 1607

华为Ascend C算子开发能力认证(中级)(个人记录,有完整代码,已通过认证,仅供参考)

本文介绍了在昇腾NPU环境下实现Sigmoid算子的完整流程。首先需要在华为云ModelArts-Notebook或开发者套件上搭建环境,要求CANN版本为8.0.0.beta1。主要内容包括:1)实现SigmoidCustom算子的kernel侧核函数代码;2)完成host侧Tiling结构体实现;3)支持Float16数据类型。代码实现部分详细展示了核函数计算流程、数据拷贝和牛顿迭代优化方法。最后介绍了编译部署流程,包括环境配置、代码上传、编译执行等步骤,并提供了提交规范说明。该实现通过调用Ascend

qq_73044452的博客 1107

JQuery获取节点的方法

1.first()方法 符合条件的第一个兄弟元素 相当于nth-child(1)2.last() 符合条件的最后一个兄弟元素3.eq(下标) 获取下标对应的元素节点对象4.parent() 获取父元素节点对象5.children() 获取所有的子元素节点对象children(子元素的范围,选择器)6.next() 获取下一个兄弟元素节点7.prev() 获取上一个兄弟元素节点8.find("要找的范围") 找到节点对应的后代节点,范围通常是选择器9.nextAll() 获取以下所有兄弟元素节点10.pre

xiaozdi的博客 1685

ValueError:Using a `device_map`, `tp_plan`, `torch.device` context manager or setting `torch.set解决方案

本文主要介绍了ValueError:Using a `device_map`, `tp_plan`, `torch.device` context manager or setting 'torch.set_default_device(device)` requires `accelerate`解决方案,希望能对使用transformers的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 3万+

SpringAI系列 - MCP篇(二) - MCP Server Boot Starter

此处需注意之前的。

罗小爬的技术宝书 1316

PyCharm 里面的 c、m、F、f、v、p 分别代表什么含义?

平时在用Pycharm的时候注意到在补全函数提示词前面有c,m,p等小分类,这些到底是什么意思呢,一起看一下吧! c代表Class:类 m代表Method:类实例方法 F代表Function:函数 f代表Field:类属性,域 v代表Variable:变量 p代表Property:python内置函数 p代表Parameter:参数 <>代表Element:元素 Directory:目录 Package:包 下面是官网解释: 网站地址:https://www.jetbrains.com/he

十二北的博客 3万+

STM32电机PID速度控制

2.3 STM32电机PID速度控制 之前的文章,完成了直流减速电机的PWM控制、电机测速。本篇文章,将实现电机的速度闭环控制。 在公众号:小白学移动机器人,发送:速度PID,即可获得本篇文章的STM32工程文件以及相关资料。 2.3.1 解决的问题 解决带编码器直流电机的速度闭环问题。 2.3.2 PID理论 将偏差的比例、积分、微分,通过线性组合构成控制量,用控制量对被控对象进行控制,这样的控制器称为PID控制器。在连续空间中,我们通常探讨模拟PID的控制原理,如图所示: 我们这里用电机速度控制为例,

「小白学移动机器人」一个专注分享移动机器人相关知识的公众号! 6万+

jquery 获取元素(父节点,子节点,兄弟节点)

一、jQuery的父节点查找方法 $(selector).parent(selector):获取父节点 $(selector).parentNode:以node[]的形式存放父节点,如果没有父节点,则返回空数组 $(selector).parents(selector):获取祖先元素 二、jQuery的兄弟节点查找方法 $(selector).prev() / $(selector).previousSibling():获取上一...

monica_517的博客 1万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

pycharm 中写代码的提示的前符号 p,m ,c,v, f 是什么意思

是自动补全的变量的类别 p:parameter 参数 m:method 方法 c:class 类 v:variable 变量 f:function 函数

#姚大姚 的博客 3802

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Pycharm书写代码时补全提示的c、m、F、f、V、P、a、<>、T等是什么意思?

Pycharm书写代码时补全提示的c、m、F、f、V、P、a、、T等是什么意思?

昊虹AI笔记 2216

在pycharm下执行python -m命令

1. “python -m” -m: run library module as a script (terminates option list) 将模块当做脚本去启动有什么用? python xxx.py python -m xxx 不同的加载py文件的方式,主要是影响sys.path这个属性 举例: test.py import sys print(sys.path[0]) 执行python xxx.py脚本运行模式 python myproj/test.py return: '/hom

haoshu1231的博客 2404

基于ESP32与离线语音的3D打印智能浮雕灯DIY全流程解析

嵌入式系统与物联网技术正推动智能家居设备向个性化、高交互性方向发展。其核心原理在于通过微控制器(如ESP32)连接传感器与执行器,并运行控制程序,实现设备智能化。这项技术的价值在于将数字制造与物理世界无缝衔接,让用户能亲手创造兼具美学与功能的智能产品。在应用场景上,它特别适合DIY爱好者、创客教育以及个性化家居装饰领域。本文聚焦于一个具体实践:如何利用3D打印技术制作浮雕灯罩,并结合ESP32微控制器与离线语音识别模块,实现一个无需网络、响应迅速的语音控制智能灯。项目详细涵盖了从3D建模、切片参数设置,到电

weixin_33871366的博客 715

Pycharm中的Python Console与Terminal

转自:https://blog.youkuaiyun.com/qq_36309480/article/details/82702021 Pycharm的下方工具栏中有两个窗口:Python Console和Terminal(如下图) 其中,Python Console叫做Python控制台,即Python交互模式;Terminal叫做终端,即命令行模式。 Python交互模式主要有两种:CPython用...

不积跬步,无以至千里 3542

PyCharm里面的c、m、F、f、v、p分别代表什么含义?

看图片: 我之前就把小写的f误认为是function,就觉得很不对劲。后来终于发现,小写的f代表类属性,即Field;大写的F才代表Function,即普通函数。 或者访问官网,查看更多符号解释: https://www.jetbrains.com/help/pycharm/symbols.html Python中函数(Function)和方法(Method)的区别: 与类和实例无...

小橙子的博客 1万+

Pycharm中c、m、F、f、v、p分别代表什么含义

c代表Class:类 m代表Method:方法 F代表Function:函数 f代表Field:域 v代表Variable:变量 p代表Property:python内置函数 p代表Parameter:参数 <>代表Element:元素 Directory:目录 Package:包 Python中函数(Function)和方法(Method)的区别: 与类和实例无绑定关系的functi...

DreamBeliever的博客 6615

PyCharm 图标 c、m、f、F、v、p 的含义

PyCharm 图标 c、m、f、F、v、p 的含义

宇宙第一小趴菜的博客 848

python代码的颜色代表什么,python中的颜色代码

Python是一种普遍使用的编程语言,以其简单易懂的语法和广泛的应用领域而闻名于世。Python代码通常以黑色文本的形式呈现,但是在编写代码时,您可以使用不同的颜色代码突出显示不同的元素适合练手的python项目。

wenangou6 2816

Python字符串前的小写字母f是什么含义和作用?

Python字符串前的小写字母f是什么含义和作用?

昊虹AI笔记 2682

leetcode.1114-按序打印-多线程案例

使用信号量Semaphore ,先初始化信号量,不允许second和third函数运行,在first函数末尾调用release函数使得计数值加一,second函数可以运行;先声明两个AtomicInteger变量并初始化为0,使用他来控制first()和second(),second() 和third()之间的顺序关系。//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,在进入second和third方法时,提前进入循环去检验变量是否被增加为1,如果是,则进入方法,否则空转。

qq_51118755的博客 626

LeetCode 1114 按序打印

需求 在多线程环境下按序打印firstsecondthird,并且保证打印顺序不会出错 使用信号量 #include<semaphore.h> class Foo { sem_t sem1; sem_t sem2; public: Foo(){ //sem_init 初始化信号量 //对信号量进行初始化; sem_init(&sem1,0,0);sem_init(&sem2,0,0); } void

ALITAAAA的博客 279
上一篇: 记一次机房断电办公室网络瘫痪的恢复经过
下一篇: Macbook利用VMwareFusion安装Centos7
T型人小付
博客等级 码龄7年 433粉丝 210原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值