线程间通信

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm=1001.2014.3001.5501

我们已经知道进程之间不能直接共享信息,那么线程之间可以共享信息吗?我们通过一个例子来验证一下。定义一个全局变量g_num,分别创建2个子线程对g_num执行不同的操作,并输出操作后的结果。代码如下: 

# -*- coding:utf-8 -*-

from threading import Thread            # 导入线程

import time

def plus():                    # 第一个线程函数

    print('-------子线程1开始------')   

    global g_num                    # 定义全局变量

    g_num += 50                # 全局变量值加50

    print('g_num is %d'%g_num)

    print('-------子线程1结束------')

def minus():                    # 第二个线程函数

    time.sleep(1)                    # 休眠1毫秒

    print('-------子线程2开始------')

    global g_num                    # 定义全局变量

    g_num -= 50                # 全局变量值减50

    print('g_num is %d'%g_num)

    print('-------子线程2结束------')

g_num = 100                     # 定义一个全局变量

if __name__ == '__main__':

    print('-------主线程开始------')

    print('g_num is %d'%g_num)

    t1 = Thread(target=plus)           # 实例化线程t1

    t2 = Thread(target=minus)         # 实例化线程t2

    t1.start()                          # 开启线程t1        

    t2.start()                          # 开启线程t2        

    t1.join()                           # 等待t1线程结束   

    t2.join()                           # 等待t2线程结束

    print('-------主线程结束------')

上述代码中,定义一个全局变量g_num,赋值为100,然后创建2个线程。一个线程将g_num增加50,一个线程将g_num减少50。如果g_num的最终结果为100,则说明线程之间可以共享数据。运行结果如图17所示。

图17  检测线程数据是否共享

从上面的例子可以得出,在一个进程内的所有线程共享全局变量,能够在不使用其他方式的前提下完成多线程之间的数据共享。

1  什么是互斥锁

由于线程可以对全局变量随意修改,这就可能造成多线程之间对全局变量的混乱操作。依然以房子为例,当房子内只有一个居住者时(单线程),他可以任意时刻使用任意一个房间,如厨房、卧室和卫生间等。但是,当这个房子有多个居住者时(多线程),他就不能在任意时刻使用某些房间,如卫生间,否则就会造成混乱。

如何解决这个问题呢?一个防止他人进入的简单方法,就是门上加一把锁。先到的人锁上门,后到的人就在门口排队,等锁打开再进去。如图18所示。

图18  互斥锁示意图

这就是“互斥锁”(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。互斥锁为资源引入一个状态:锁定和非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”时,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

2  使用互斥锁

在threading模块中使用Lock类可以方便地处理锁定。Lock类有2个方法:acquire()锁定和release()释放锁。示例用法如下:

mutex = threading.Lock()            # 创建锁

mutex.acquire([blocking])             # 锁定

mutex.release()                # 释放锁

参数说明如下:

l  acquire([blocking]):获取锁定,如果有必要,需要阻塞到锁定释放为止。如果提供blocking参数并将它设置为False,当无法获取锁定时将立即返回False;如果成功获取锁定则返回True。

l  release():释放一个锁定。当锁定处于未锁定状态时,或者从与原本调用acquire()方法的不同线程调用此方法,将出现错误。

图标 (35)

  实例06  使用互斥锁实现多人同时订购电影票

下面,通过一个示例学习一下如何使用互斥锁。这里使用多线程和互斥锁模拟实现多人同时订购电影票的功能,假设电影院某个场次只有100张电影票,10个用户同时抢购该电影票。每售出一张,显示一次剩余的电影票张数。代码如下: 

from threading import Thread,Lock

import time

n=100                     # 共100张票                     

def task():

    global n

    mutex.acquire()                     # 上锁

    temp=n                              # 赋值给临时变量

    time.sleep(0.1)                     # 休眠0.1秒

    n=temp-1                            # 数量减1

    print('购买成功,剩余%d张电影票'%n)

    mutex.release()                     # 释放锁

if __name__ == '__main__':

    mutex=Lock()                        # 实例化Lock类

    t_l=[]                              # 初始化一个列表

    for i in range(10):        

        t=Thread(target=task)           # 实例化线程类   

        t_l.append(t)                   # 将线程实例存入列表中

        t.start()                       # 创建线程

    for t in t_l:              

        t.join()                # 等待子线程结束   

上述代码中,创建了10个线程,全部执行task()函数。为解决资源竞争问题,使用mutex.acquire()函数实现资源锁定,第一个获取资源的线程锁定后,其他线程等待mutex.release()解锁。所以每次只有一个线程执行task()函数。运行结果如图19所示。

图19  模拟购票功能

注意:使用互斥锁时,要避免死锁。在多任务系统下,当一个或多个线程等待系统资源,而资源又被线程本身或其他线程占用时,就形成了死锁,如图20所示。

图20  死锁示意图

3  使用队列在线程间通信

我们知道multiprocessing模块的Queue队列可以实现进程间通信,同样在线程间,也可以使用Queue队列实现线程间通信。不同之处在于我们需要使用queue模块的Queue队列,而不是multiprocessing
模块的Queue队列,但Queue的使用方法相同。

使用Queue在线程间通信通常应用于生产者消费者模式。产生数据的模块称为生产者,而处理数据的模块称为消费者。在生产者与消费者之间的缓冲区称之为仓库。生产者负责往仓库运输商品,而消费者负责从仓库里取出商品,这就构成了生产者消费者模式。下面通过一个示例学习一下使用Queue在线程间通信。

图标 (35)

  实例07  使用队列模拟生产者消费者模式

定义一个生产者类Producer,定义一个消费者类Consumer。生成者生成5件产品,依次写入队列,而消费者依次从队列中取出产品,代码如下: 

from queue import Queue

import random,threading,time

# 生产者类

class Producer(threading.Thread):

    def __init__(self, name,queue):

        threading.Thread.__init__(self, name=name)

        self.data=queue

    def run(self):

        for i in range(5):

            print("生成者%s将产品%d加入队列!" % (self.getName(), i))

            self.data.put(i)

            time.sleep(random.random())

        print("生成者%s完成!" % self.getName())

# 消费者类

class Consumer(threading.Thread):

    def __init__(self,name,queue):

        threading.Thread.__init__(self,name=name)

        self.data=queue

    def run(self):

        for i in range(5):

            val = self.data.get()

            print("消费者%s将产品%d从队列中取出!" % (self.getName(),val))

            time.sleep(random.random())

        print("消费者%s完成!" % self.getName())

if __name__ == '__main__':

    print('-----主线程开始-----')

    queue = Queue()                    # 实例化队列

    producer = Producer('Producer',queue)      # 实例化线程Producer,并传入队列作为参数

    consumer = Consumer('Consumer',queue)       # 实例化线程Consumer,并传入队列作为参数

    producer.start()                    # 启动线程Producer

    consumer.start()                    # 启动线程Consumer

    producer.join()                 # 等待线程producer结束

    consumer.join()                 # 等待线程consumer结束

    print('-----主线程结束-----')

运行结果如图21所示。

图21  使用Queue在线程间通信

注意:由于程序中使用了random.random()函数生成0-1之间的随机数,所以读者的运行结果可能与图21不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/30381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【腾讯云智笔试题——分苹果两种解法】

文章目录 题目描述解题思路:疑惑解析代码中明明是两个嵌套的for循环时间复杂度为什么是O(m)? 题目描述 有m个苹果,n个小孩。每个小孩都有一个编号,小明的编号是。要尽量公平的分苹果,相邻编号的…

C++ 类与对象的使用要点(超详细解析,小白必看系列)

1.面向过程和面向对象初步认识 C语言是面向过程的语言,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题 例如:洗衣服 C是基于对象的,关注的是对象,将一件事拆分成不同的对象,靠对…

[NewStarCTF 2023 公开赛道]R!C!E!

好久没写了。今天儿弄一个rce 题很直接&#xff0c;好久没这么直白的题了&#xff0c;看源码 <?php highlight_file(__FILE__); if(isset($_POST[password])&&isset($_POST[e_v.a.l])){$passwordmd5($_POST[password]);$code$_POST[e_v.a.l];if(substr($password…

amov无人机连接;+数据传输;啊啊啊啊啊

socket传输数据: 局域网连接 连接---通信(命令行直接;)--- 传输数据(socket)--传输内容:launch文件; qgc连接; 1.局域网下的通信 1.1 局域网 厂家提供的方式是通过Homer图数传工具(硬件)构建的amov局域网实现通信连接. 好处是通信距离足够长,支持150m;坏处是"局部&qu…

电脑提示d3dcompiler_47.dll丢失的解决方法,实测靠谱的5种方法

在计算机使用过程中&#xff0c;缺失d3dcompiler_47.dll这一系统文件是一个常见问题&#xff0c;尤其是对于游戏和图形密集型应用程序用户来说尤为重要。这个文件是DirectX软件工具包的一部分&#xff0c;主要用于处理图形渲染的应用程序接口的核心元素。当你在运行游戏或某些软…

如何定制Spring的错误json信息

一&#xff0c;前言 相信很多同学都有遇到过这样的spring错误信息。 在我们没有做catch处理时或者做全局的exceptionHandle时&#xff0c;Spring遇到抛出向外的异常时&#xff0c;就会给我们封装返回这么个格式的异常信息。 那么问题来了&#xff0c;我们能否对这个返回增加错…

网络安全从入门到精通(特别篇I):应急响应案例

蓝队应急响应实战 1. 应急响应1. 应急响应 获取当前WEB环境的组成架构(语言,数据库,中间件,系统等) 分析思路: 1、利用时间节点筛选日志行为 2、利用已知对漏洞进行特征筛选 3、利用后门查杀进行筛选日志行为 #内容点: 应急响应: 1、抗拒绝服务攻击防范应对指南 2、勒…

day13 二叉树的遍历

一、二叉树的递归遍历 题目链接&#xff1a; 144.二叉树的前序遍历(opens new window)145.二叉树的后序遍历(opens new window)94.二叉树的中序遍历 文章讲解&#xff1a;https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%80%92%E5%BD%92%E9%81%8D%E5%8E…

Go源码--sync库(3):sync.Pool(2)

回收 回收其实就是将 pool.local 置为空 可以让垃圾回收器回收 我们来看下 源码 func init() {// 将 poolCleanup 注册到 gc开始前的准备工作处理器中在 STW时执行runtime_registerPoolCleanup(poolCleanup) }这里注册了清理程序到GC前准备工作 也就是发生GC前需要执行这段代…

鸿蒙HarmonyOS实战:渲染控制、路由案例

条件渲染 简单来说&#xff0c;就是动态控制组件的显示与隐藏&#xff0c;类似于vue中的v-if 但是这里写法就是用if、else、else if看起来更像是原生的感觉 效果 循环渲染 我们实际开发中&#xff0c;数据一般是后端返回来的对象格式&#xff0c;对此我们需要进行遍历&#…

Nginx 搭建域名访问环境

1.Nginx配置文件 server {listen 80;server_name www.gulimall.com;#charset koi8-r;#access_log /var/log/nginx/log/host.access.log main;location / {proxy_pass http://192.168.232.1:10001;}#error_page 404 /404.html;# redirect server error p…

信息论与大数据安全知识点

文章目录 第一章 绪论&#xfffc;大数据概述大数据安全与加密技术 安全存储与访问控制技术访问控制概念早期的四种访问控制模型局限性总结 大数据场景下的访问控制技术 安全检索技术密文检索基础 安全处理技术同态加密 隐私保护技术 第一章 绪论&#xfffc; 大数据概述 大数…

分班查询,一键发布,老师们都在用的分班查询系统

老师们开学季马上又要到了&#xff0c;回想起了每年埋头苦干&#xff0c;对着一堆堆的学生名单&#xff0c;一个个手动分配班级&#xff0c;再一个个通知家长和学生的日子&#xff0c;那种手忙脚乱&#xff0c;生怕出错的紧张感&#xff0c;是不是还历历在目&#xff1f;每次分…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA 的幸运游戏(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

三个“消失” 折射债市新变化

资金分层现象逐步消失&#xff1b;低位的DR007利率已不常见&#xff1b;债市中一度盛行的“滚隔夜”也在逐渐减少。 当前&#xff0c;债券市场正在出现一系列显著变化&#xff1a;资金分层现象逐步消失&#xff1b;低位的DR007利率已不常见&#xff1b;债市中一度盛行的“滚隔…

STM32自己从零开始实操06:无线电路原理图

一、WIFI 模块电路设计 1.1指路 延续使用 ESP-12S 芯片&#xff0c;封装 SMD 16x24mm。 实物图 原理图与PCB图 2.2电路图 电路较为简单&#xff0c;如下图&#xff1a; 2.2.1引脚说明 序号引脚名称描述1RST复位复位引脚&#xff0c;低电平有效3EN使能芯片使能端&#xff0c…

【华为HCIA数通网络工程师真题-数据通信与网络基础】

文章目录 选择题判断题 选择题 1、在 VRP 平台上&#xff0c;可以通过下面哪种方式访向上条历史命令&#xff1f; 上光标 &#xff08;ctrlU 为自定义快捷键&#xff0c;ctrlP 为显示历史缓存区的前一条命令&#xff0c;左光标为移动光标&#xff09; 2、主机 A &#xff08;1…

TVS的原理及选型

目录 案例描述 TVS管的功能与作用&#xff1a; TVS选型注意事项&#xff1a; 高速TVS管选型 最近项目中遇到TVS管选型错误的问题。在此对TVS的功能及选型做一个分享。 案例描述 项目中保护指标应为4-14V&#xff0c;而选型的TVS管位SMJ40CA&#xff0c;其保护电压为40V未…

python创建虚拟环境venv

为什么要创建虚拟环境 使用python创建虚拟环境是为了让项目的依赖隔离开来&#xff0c;互不干扰&#xff0c;使得每个项目都运行在一个独立的Python环境中。 创建虚拟环境 1. 命令行创建 step1. 创建 # 1. 进入到你的项目目录中 cd myproject # 使用python创建一个虚拟环境…

音视频入门基础:H.264专题(1)——H.264官方文档下载

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…