树莓派4B学习笔记14:Python多线程编程_线程间的同步通信_(锁‘threading.Lock’)

news2024/11/27 11:41:55

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi)

 本人所用树莓派4B 装载的系统与版本如下:

 版本可用命令 (lsb_release -a) 查询:

 Opencv 版本是4.5.1:

 今日学习树莓派与Python的多进程编程_线程间同步通信

 文章提供测试代码讲解,整体代码贴出、测试效果图

目录

建立简单的多线程:

创建一个新的线程对象:

 代码示例:

测试效果与解释:

join方法的使用:

线程间的同步与通信:

问题代码与说明:

竞态条件:

使用锁同步对共享资源的访问:

代码改进提示:

网上学习网址贴出:


建立简单的多线程:

创建一个新的线程对象:

threading.Thread 类用于创建一个新的线程对象。

这个类接受几个参数,其中最重要的是 targetargs

target

target 参数是一个可调用的对象(如函数或方法),当线程启动时,这个对象会被线程执行。在这个例子中,target 被设置为 counter 函数,所以新创建的线程会执行 counter 函数。

args

args 参数是一个元组,用于给 target 函数传递参数。在这个例子中,args 是一个只包含一个元素的元组,即字符串 "计数A"。当 t1 线程启动并调用 counter 函数时,它会传递这个字符串作为参数。

需要注意的是,由于 args 需要是一个元组,所以即使只传递一个参数,也需要用逗号 , 来表明它是一个元组(在只有一个元素的元组后面加上逗号是一个常见的约定)。

 代码示例:

以下代码简单示例了如何编写一个简单的多线程程序:

# -*- coding: utf-8 -*-  
import time  
import threading  
  
def counter(name):  
    for i in range(5):  
        print(name, i)  
        time.sleep(1)  

t1 = threading.Thread(target=counter, args=("计数A",))  
t2 = threading.Thread(target=counter, args=("计数B",))  
  
t1.start()  
t2.start() 
  
print("主线程完成")

测试效果与解释:

我们发现这个程序中一共三条线程:t1、t2、主线程

主线程因为只有一句prtint,因此在t1、t2计数第一次后就结束了

同时:t1、t2俩个线程是一起运行的

join方法的使用:

如果想主线程等待其余线程执行完,则可以使用join方法:

这样主线程就会在俩个子线程结束后才执行:

线程间的同步与通信:

问题代码与说明:

下面展示一段问题代码,该段代码有个全局变量N,程序试图通过多个线程对其进行加操作

从而达到快速加到某个值的效果:

# -*- coding: utf-8 -*-  
import time  
import threading  

# 定义一个全局变量,整数类型  
N=0

def Add(name):
    global N                  # 如果想在函数内部修改全局变量的值,需要使用global关键字  
    for i in range(10000):  
        N += 1                 # 使用 += 来递增全局变量

t1 = threading.Thread(target=Add, args=("累加1",))  
t2 = threading.Thread(target=Add, args=("累加2",))  
t3 = threading.Thread(target=Add, args=("累加3",))  
  
t1.start()  
t2.start() 
t3.start()   

t1.join() 
t2.join() 
t3.join()
 
print("主线程完成 N的最终值为:{}".format(N))

虽然我们发现这个操作结果是预期的30000,并未出现更改全局变量 N 冲突的情况,主要是因为线程间对 N 的增加操作是原子的(atomic)。在Python中,对于整数类型(int)和不可变类型(如元组、字符串等),在CPython实现中,简单的赋值和算术操作通常是原子的。

但是,请注意,这种情况并不意味着多线程环境下对全局变量的修改总是安全的。对于更复杂的操作或可变类型(如列表、字典等),多线程访问和修改可能会出现问题,例如数据竞争(data race)、死锁(deadlock)或条件竞争(condition race)。

竞态条件:

在以下给出的网址中的视频内就展示了对全局变量的非原子操作导致的加减输出混乱的情况

多线程编程:一次性搞懂线程同步机制_哔哩哔哩_bilibili

我们发现他的多线程程序并未累加全局变量到预期的值10万,而是运行出了六次不同的结果

使用锁同步对共享资源的访问:

threading.Lock

Lock 对象提供了一种方法来同步线程,以确保在任何时候只有一个线程可以访问共享资源。

下面是修改后的代码,其中添加了 Lock 来保护对全局变量 N 的访问:

# -*- coding: utf-8 -*-    
import time    
import threading    
  
# 定义一个全局变量,整数类型  
N = 0  
# 定义一个全局锁  
lock = threading.Lock()  
  
def Add(name):  
    global N, lock  
    for i in range(10000):  
        # 在修改N之前先获取锁  
        with lock:  
            N += 1                 # 使用 += 来递增全局变量  
  
t1 = threading.Thread(target=Add, args=("累加1",))    
t2 = threading.Thread(target=Add, args=("累加2",))    
t3 = threading.Thread(target=Add, args=("累加3",))    
    
t1.start()    
t2.start()   
t3.start()     
  
t1.join()   
t2.join()   
t3.join()  
   
print("主线程完成 N的最终值为:{}".format(N))

添加了一个名为 lockthreading.Lock 对象。

Add 函数中,我使用 with lock: 语句块来确保在修改 N 时锁是获取的。

这个 with 语句块会在进入时自动调用 lock.acquire() 来获取锁,并在退出时自动调用 lock.release() 来释放锁。

这样,在任何时候只有一个线程可以执行 N += 1 这行代码。

现在,即使你运行多个线程来同时修改 N,也不会出现数据不一致的问题,因为每个线程在修改 N 之前都会先获取锁,确保其他线程在此期间不会修改 N

运行效果展示:

代码改进提示:

但频繁地上锁解锁十分浪费资源,可以先定义一个局部变量代为运算,最后赋值给全局:

这样我们只需一次上锁与释放的操作就可以做到目标结果:

修改前繁琐的部分:

会进行高达10000次的:获取锁、释放锁的操作

修改的部分:

只进行了一次获取、释放锁的操作

整体代码:

# -*- coding: utf-8 -*-    
import time    
import threading    
  
# 定义一个全局变量,整数类型  
N = 0  
# 定义一个全局锁  
lock = threading.Lock()  
  
def Add(name):  
    global N, lock
    n=0
    for i in range(10000):  
        n+=1
    # 在修改N之前先获取锁  
    with lock:  
        N += n                 # 使用 += 来递增全局变量  
  
t1 = threading.Thread(target=Add, args=("累加1",))    
t2 = threading.Thread(target=Add, args=("累加2",))    
t3 = threading.Thread(target=Add, args=("累加3",))    
    
t1.start()
t2.start()
t3.start()
  
t1.join()
t2.join()
t3.join()  
   
print("主线程完成 N的最终值为:{}".format(N))

运行结果:

网上学习网址贴出:

100秒学会Python多线程threading_哔哩哔哩_bilibili

多线程编程:一次性搞懂线程同步机制_哔哩哔哩_bilibili

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

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

相关文章

capitalize()方法——字符串首字母转换为大写

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 capitalize()方法用于将字符串的首字母转换为大写,其他字母为小写,例如图1所示的效果。 图1 字符串首字母大写效果…

安全技术和防火墙(2)

安全技术和防火墙 文章目录 安全技术和防火墙安全技术防火墙通信的五大要素和四大要素四表五链*控制类型管理选项:匹配条件实验步骤关闭防火墙 添加规则指定IP地址指定多个ip指定端口拒绝访问nginx删除规则修改策略修改链拒绝整个网段禁止多个端口匹配mac地址 iptab…

代理IP对SEO影响分析:提升网站排名的关键策略

你是否曾经为网站排名难以提升而苦恼?代理服务器或许就是你忽略的关键因素。在竞争激烈的互联网环境中,了解代理服务器对SEO的影响,有助于你采取更有效的策略,提高网站的搜索引擎排名。本文将为你详细分析代理服务器在SEO优化中的…

自动化代码规范检查--Sonarqube部署

参考文档 官方文档安装数据库 官方给出几种数据库: # 我们选用postgres, 拉取镜像 docker pull postgres:16.0# 创建存储卷 docker volume create postgresql-data# 运行容器 docker run -d --name sonarqube-postgres \-p 5432:5432 \-e POSTGRES_DB=sonar_DB \-e POSTGRE…

pyqt5 制作视频剪辑软件,切割视频

该软件用于切割视频,手动选取视频片段的起始帧和结束帧并保存为json文件。gui界面如下:包含快进、快退、暂停等功能, 代码如下: # coding=UTF-8 """ theme: pyqt5实现动作起始帧和结束帧的定位,将定位到的帧数保存json文件 time: 2024-6-27 author: cong…

vs2017调试MFC源码与dll版本不匹配

如上图,使用VS2017调试MFC源码,提示源码与dll不匹配。 经过一番折腾终于找到了原因:同时安装了vs2017、vs2022,结果加载的mfc140ud.dll不是vs2017的,而是vs2022的,主版本号虽然都是14,但小版本…

uniapp加载打点点效果

uniapp加载打点点效果 背景实现思路代码实现尾巴 背景 为了增加系统的交互性,我们在加载数据时通常会增加一些loading动效,但是在某些场景下只需要一些简单文字提醒。比如说使用【加载中】或者【loading】等字段,但是写静态的字符又显得交互…

探索未来的AI革命:GPT-5的即将登场

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

-bash: /snap/bin/docker: 没有那个文件或目录

-bash: /snap/bin/docker: 没有那个文件或目录 解决办法 export PATH$PATH:/usr/bin/docker然后,重新加载配置文件 source ~/.bashrc

43.SO_BACKLOG

属于ServerSocketChannel参数 SO_BACKLOG 设置的过小,高峰期有很多连接来了,就会被拒绝,报拒绝连接错误。 控制全连接队列的大小,可以容下适量连接。所以SO_BACKLOG设置的要大一些。 serverBootstrap.option(ChannelOption.SO_B…

AI 卖货主播大模型:Streamer-Sales 销冠!MoneyPrinterTurbo :简直就是营销号的梦想工具!

AI 卖货主播大模型:Streamer-Sales 销冠!MoneyPrinterTurbo :简直就是营销号的梦想工具! AI 卖货主播大模型:Streamer-Sales 销冠! 项目简介 Streamer-Sales 销冠 —— 卖货主播大模型 是一个能够根据给定的商品特点从激发用户购…

EXCEL返回未使用数组元素(未使用值)

功能简介: 在我们工作中,需要在EXCEL表列出哪些元素(物品或订单)已经被使用了(或使用了多少次),哪些没有被使用。 当数量过于庞大时人工筛选或许不是好办法,我们可以借助公式&…

encode()方法——编码字符串

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 编码是将文本(字符串)转换成字节流,Unicode格式转换成其他编码格式。在Python中提供了encode()方法&am…

数据库对比脚本,java如何对比两个数据库的表字段的不同

因为有时候开发环境和 测试环境,有时候会有不同的数据库表,比如有些加字段了,所以这个脚本就实现了对比两个数据库连接的数据库到底哪里不一样,输出到控制台 package com.junfun.pms;import lombok.extern.slf4j.Slf4j;import ja…

【UE5.3】笔记6-第一个简单小游戏

打砖块小游戏: 1、制造一面砖块组成的墙 在关卡中放置一个cube,放这地面上,将其转换成蓝图类,改名BP_Cube,更换砖块的贴图,按住alt键进行拷贝,堆出一面墙,复制出来的会很多,全选移动…

【深度学习】卷积神经网络CNN

李宏毅深度学习笔记 图像分类 图像可以描述为三维张量(张量可以想成维度大于 2 的矩阵)。一张图像是一个三维的张量,其中一维代表图像的宽,另外一维代表图像的高,还有一维代表图像的通道(channel&#xff…

# [0628] Task04 DQN 算法及进阶

easy-rl PDF版本 笔记整理 P6 - P8 joyrl 比对 补充 P7 - P8 相关 代码 整理 待整理 !! 最新版PDF下载 地址:https://github.com/datawhalechina/easy-rl/releases 国内地址(推荐国内读者使用): 链接: https://pan.baidu.com/s/1i…

BigInteger 和 BigDecimal(java)

文章目录 BigInteger(大整数)常用构造方法常用方法 BigDecimal(大浮点数)常用构造方法常用方法 DecimalFormat(数字格式化) BigInteger(大整数) java.math.BigInteger。 父类:Number 常用构造方法 构造方法:BigIntege…

实操Nginx+Tomcat多实例部署,实现负载均衡和动静分离

192.168.10.10 192.168.10.20 192.168.10.30 location ~ \.jsp$ {proxy_pass http://192.168.10.50:8080;} location ~ \.(jsp|html)$ {root /usr/share/nginx/html;}192.168.10.40和192.168.10.50用脚本完成搭建此处安装附上脚本: #!/bin/bash# 定义变量 JDK_PACKA…

破解对LabVIEW的偏见

LabVIEW被广泛应用于科学研究、工程测试和自动化控制领域,具有专业性和高效的开发能力。尽管有人对其存在偏见,认为不如C语言,但LabVIEW的图形化编程、强大集成能力、丰富社区支持和专业功能,使其在许多实际应用中表现出色。通过多…