线程间通信

自学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不同。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++设计模式——Proxy代理模式

一,代理模式简介 代理模式是一种 结构型设计模式,该模式通过引入一个新的代理对象Proxy,来间接访问原始对象,从而使访问方式变得灵活和可控。 代理对象的设定减少了客户端与真实对象之间的直接交互。 通过引入代理对象来间接访问原…

【diffusers 极速入门(二)】如何得到扩散去噪的中间结果?Pipeline callbacks 管道回调函数

本文是对 Hugging Face Diffusers 文档中关于回调函数的翻译与总结,: 管道回调函数 在管道的去噪循环中,可以使用callback_on_step_end参数添加自定义回调函数。该回调函数在每一步结束时执行,并修改管道属性和变量,以…

2024青海三支一扶招1910人7月6日笔试

📢2024年青海省三支一扶计划招募1910人公告已发布! 小🀄️帮大家整理好了考试关键时间点: ★ 报名时间:6月20日至6月25日 ★ 报名网站:青海省人事考试信息网(www.qhpta.com) ★ 网上…

每日一练:攻防世界:miao~

给了一张jpg图片 没发现什么特别,放到winhex中查看也没思路。 放到kali里面foremost分离文件试试,结果分离出个wav音频文件 直接放到 audycity看看频谱图 发现字符串,但是没有其他信息。可能是密钥之类的。到这里我就卡住了,看…

vue3面试题八股集合——2024

vue3比vue2有什么优势? 性能更好,打包体积更小,更好的ts支持,更好的代码组织,更好的逻辑抽离,更多的新功能 描述Vu3生命周期 Options API的生命周期: beforeCreate: 在实例初始化之后、数据观…

喜讯!昂辉科技通过2024年度重点产业链企业(第一批)认定

日前,合肥市推进战略性新兴产业发展工作委员会办公室公布了 2024年度重点产业链企业(第一批)新入库名单(集成电路、新型显示、网络与信息安全、城市安全、空天信息、新能源汽车和智能网联汽车、生物医药、新材料、高端装备、节能环…

【PyQt5】一文向您详细介绍 QHBoxLayout() 的作用

【PyQt5】一文向您详细介绍 QHBoxLayout() 的作用 下滑即可查看博客内容 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介:985高校的普通本硕&a…

【IPython的使用技巧】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

GLSB是什么?带你深入了解GLSB核心功能

伴随互联网的快速发展,大型企业等组织单位通过建设多数据中心,以提升用户体验。然而想要在多个数据中心实现流量的智能管理,提高网站的可靠性和可用性,则需要全局服务器负载均衡技术——GLSB的助力。GLSB是什么?它又有…

算法金 | 再见!!!梯度下降(多图)

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 接前天 李沐:用随机梯度下降来优化人生! 今天把达叔 6 脉神剑给佩奇了,上 吴恩达:机器…

解决MacOS docker 拉取镜像慢的问题

docker官网:https://docker.p2hp.com/get-started/index.html 下载完成之后,拉取镜像速度慢,问题如下: 解决方法 配置阿里源:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors在docker desktop里面设置…

【C++入门(4)】引用、内联函数、auto

一、引用与类型转换 我们看下面这个例子。 用 int & 给 double 类型的变量起别名,编译器报错: int main() {double b 3.14;int a b;int& x b;return 0; } 用 const int & 给 double 类型的变量起别名,成功: in…

Spark日志有哪些?

spark.log:记录作业运行日志,包括Spark框架内部日志和用户通过日志接口输出的日志。 executor 启动结束日志: job,stage,task提交结束日志: pmap.log:周期性地截取Driver或Executor的pmap和…

element--el-table添加合计后固定列x轴滚动条无法滚动问题

效果图 改变固定列滚轮高度问题 解决文章 解决方案 使用到的参数 pointer-events 属性用来控制一个元素能否响应鼠标操作,常用的关键字有 auto 和 none pointer-events: none; 让一个元素忽略鼠标操作 pointer-events: auto; 还原浏览器设定的默认行为 代码演示 添…

C++11(1)

这一节介绍一些C11个人认为比较常用的部分 文章目录 1.{}列表初始化2.initializer_list3.auto、decltype、nullptr关键字4.范围for5.左值引用、右值引用、万能引用(完美转发)6.lambda表达式7.新的类功能8.可变参数模板9.包装器 1.{}列表初始化 C98中,标准允许使用花…

Wireshark v4 修改版安装教程(免费开源的网络嗅探抓包工具)

前言 Wireshark(前称Ethereal)是一款免费开源的网络嗅探抓包工具,世界上最流行的网络协议分析器!网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark网络抓包工具使用WinPCAP作为…

【ARM Cache 及 MMU 系列文章 6.5 -- 如何进行 Cache miss 统计?】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 ARM Cache Miss 统计Cache 多层架构简介Cache 未命中的类型Cache 未命中统计Cache miss 统计代码实现Cache Miss 统计意义ARM Cache Miss 统计 在ARMv8/v9架构中,缓存未命中(Cache …

使用MAT定位线上OOM问题

目录 1.什么是OOM? 2.发生的可能原因 3.常见类型的OOM 4.如何定位问题? 4.1 获取dump文件 4.2 MAT分析 「Leak Suspects」泄露嫌疑 「Histogram」直方图 「dominator tree」支配树 「thread overview」线程视图 目录 1.什么是OOM? 2.发生的可能原因 …

完整迁移方案+工具:Citrix替换,无感迁移!

随着用户的替换进程进入到演进的阶段,用户面临的重大挑战包括: (1)大量数据的迁移需要精确规划,以避免数据丢失或损坏; (2)迁移效率低下,不仅会增加迁移成本,…