当前位置: 首页 > news >正文

Python多线程知多少

目录

目标

Python版本

官方文档

概述

线程

守护线程

线程同步

事件对象(Event Object)

实战

创建线程的基本语法

阻塞线程

守护线程

线程同步的方法

互斥锁(排他锁)

信号量(Semaphore)

事件对象(Event Object)

基本语法

生产者和消费者(不推荐)

队列(queue)


目标

        掌握多线程的基本概念和使用方法,包括:创建线程、线程同步、线程通信、守护线程、事件对象、队列。


Python版本

        Python 3.9.18


官方文档

基于线程的并行https://docs.python.org/3.9/library/threading.html

线程池https://docs.python.org/3.9/library/concurrent.futures.html


概述

线程

定义

        线程(Thread)是操作系统调度的最小单位,是进程(Process)内部的执行单元。线程与进程的主要区别在于,进程是资源分配的基本单位,而线程是CPU调度的基本单位

        多线程是指在同一个进程中并发执行多个线程,每个线程可以独立执行任务。Python使用threading模块提供多线程支持。

生活中的单线程案例

        餐厅中如果只有一个厨师,则该厨师需要依次完成以下工作:接收订单->准备食材->烹饪->装盘。如果餐厅生意火爆,则客人必然会等待很久。

生活中的多线程案例

        餐厅有多个厨师,则每个厨师只需要负责制作他拿手的菜,如果餐厅生意火爆,每个厨师都会忙碌起来,但客人并不会等待太久。

官方文档

CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.

        从官方文档了解到,由于全局解释器锁(GIL)的限制,Python的多线程通常并不适用于CPU密集型的任务,但对于IO密集型任务(如文件操作、网络请求等)仍然是非常有效的。这其实说明了Python的多线程往往不是并行调度。


守护线程

官方文档

Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.

        线程可以被标记为守护线程。它的特点是:如果Python程序剩下守护线程和主线程,则主线程会立刻退出,所以守护线程的生命周期依赖主线程。因此守护线程适合做一些辅助工作,如:日志、心跳、监控等。


    线程同步

    1. 线程同步的目的是确保共享资源在同一时刻只能被一个线程访问,从而避免出现数据冲突或不一致的情况。
    2. 线程同步是实现线程安全的其中一种方法。
    3. 线程同步通常用锁来实现,其中互斥锁是最基本的同步机制。

    事件对象(Event Object)

    官方文档

    This is one of the simplest mechanisms for communication between threads: one thread signals an event and other threads wait for it.

    An event object manages an internal flag that can be set to true with the set() method and reset to false with the clear() method. The wait() method blocks until the flag is true.

            这是线程之间通信的最简单机制之一:一个线程发出事件信号,而其他线程等待该信号。Event对象维护一个内部标识,通过调用各种方法实现线程通信:

    • event.set()将事件状态设为 True,所有 wait() 的线程立即执行。
    • event.clear()将事件状态设为 False,使 wait() 进入阻塞状态。
    • event.wait()如果事件状态是 False,阻塞线程,直到 set() 被调用。可以设置timeout(超时时间)参数,但是是秒。

            这里要分清楚clear()和wait()的具体作用:wait()的作用是检查Event是否为True,为True则线程继续运行,为False则线程阻塞。由此看来,Event对象调用wait()不一定会阻塞线程。只有Event对象调用clear()后,再调用wait()才可以阻塞线程


    实战

    创建线程的基本语法

    import threading
    import timedef fun_1():for i in range(5):print(f"fun_1方法开始执行。{i}")time.sleep(2)
    def fun_2():for i in range(6):print(f"fun_2方法开始执行。{i}")time.sleep(1)
    t1=threading.Thread(target=fun_1,name="线程1");
    t2=threading.Thread(target=fun_2,name="线程2");
    t1.start()
    t2.start()
    print("主线程开始继续执行。")

    输出结果 

    fun_1方法开始执行。0
    fun_2方法开始执行。0
    主线程开始继续执行。
    fun_2方法开始执行。1
    fun_1方法开始执行。1
    fun_2方法开始执行。2
    fun_2方法开始执行。3
    fun_1方法开始执行。2
    fun_2方法开始执行。4
    fun_2方法开始执行。5
    fun_1方法开始执行。3
    fun_1方法开始执行。4


    阻塞线程

            在上述案例中,我们可以看到子线程还未执行完任务,主线程就已经继续向下执行了。能否让主线程等待子线程执行完了才向下运行。我们可以使用join()方法实现这种功能,该方法有timeout这个超时参数,如果超过了这个时间子线程还未完成任务,主线程将不再等待,继续向下运行。

    import threading
    import timedef fun_1():for i in range(5):print(f"fun_1方法开始执行。{i}")time.sleep(2)
    def fun_2():for i in range(6):print(f"fun_2方法开始执行。{i}")time.sleep(1)
    t1=threading.Thread(target=fun_1,name="线程1");
    t2=threading.Thread(target=fun_2,name="线程2");
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("主线程开始继续执行。")

    输出结果 

    fun_1方法开始执行。0
    fun_2方法开始执行。0
    fun_2方法开始执行。1
    fun_1方法开始执行。1
    fun_2方法开始执行。2
    fun_2方法开始执行。3
    fun_1方法开始执行。2
    fun_2方法开始执行。4
    fun_2方法开始执行。5
    fun_1方法开始执行。3
    fun_1方法开始执行。4
    主线程开始继续执行。


    守护线程

    import threading
    import timedef fun_1():for i in range(5):print(f"fun_1方法开始执行。{i}")time.sleep(2)t1=threading.Thread(target=fun_1,name="线程1");
    #设置线程为守护线程。
    t1.setDaemon(True)
    t1.start()print("主线程开始继续执行。")

     输出结果 

    fun_1方法开始执行。0
    主线程开始继续执行。


    线程同步的方法

    互斥锁(排他锁)

            同一时间只有一个线程获取锁,其他线程会被阻塞。

    import threadingcount = 0
    #互斥锁(排他锁)
    lock = threading.Lock()def increment():global countfor _ in range(1000000):with lock:count += 1if __name__ == '__main__':t1 = threading.Thread(target=increment)t2 = threading.Thread(target=increment)t1.start()t2.start()t1.join()t2.join()#正确结果是2000000print(count)

    注意

            with lock是语法糖,相当于下面的代码:

    def increment():global countfor _ in range(1000000):lock.acquire()  # 手动获取锁try:count += 1finally:lock.release()  # 确保无论如何都释放锁,防止死锁

    信号量(Semaphore)

            多信号量不能保证线程同步,多信号量只能保证并发量。但是单个信号量的效果等同于互斥锁,可以作为线程同步的方法。

    import threadingcount = 0
    semaphore=threading.Semaphore(1)def increment():global countfor _ in range(1000000):with semaphore:count += 1if __name__ == '__main__':t1 = threading.Thread(target=increment)t2 = threading.Thread(target=increment)t1.start()t2.start()t1.join()t2.join()#正确结果是2000000print(count)

    注意

            with semaphore是语法糖,相当于下面的代码:

    def increment():global countfor _ in range(1000000):semaphore.acquire()try:count += 1finally:semaphore.release()

    事件对象(Event Object)

    基本语法

    import threading
    import timeevent = threading.Event()
    def fun():print("fun方法开始执行……")#如果设置了超时时间,则wait会在超时后放行。但事件状态仍然为False#event.wait(1)print("事件状态:", event.is_set())event.wait()print("事件状态:",event.is_set())t=threading.Thread(target=fun)
    t.start()
    time.sleep(3)
    print("事件状态:",event.is_set())
    print("事件对象设置标识为True。")
    event.set()
    print("事件状态:",event.is_set())
    t.join()
    print("主线程结束。")

    生产者和消费者(不推荐)

            事件对象是线程间一种简单的通信机制,现在我们就用事件对象实现生产者&消费者模型,具体需求:每次生产出一只烤鸭,就会被等待的消费者消费。

    分析

            虽然下面的代码可以实现每生产一只烤鸭就会立刻消费一只烤鸭。但如果把代码中生产烤鸭的睡眠时间注释掉,烤鸭生产速度就会很快,最终导致烤鸭消费不完。对应实际业务,生产者产生的消息太快,消费者往往来不及消费,导致消息丢失因为Event只能使用标识来阻塞或唤醒线程,而不能存储多个事件。这时我们需要用队列(queue)

    import random
    import threading
    import timedef produce_duck(event):count=0while True:#随机睡眠一段时间time.sleep(random.uniform(1, 3))count +=1print(f"成功生产出了第{count}只烤鸭。")#生产消息后立刻发出信号。event.set()def consume_duck(event):count=0while True:event.wait()count+=1print(f"消费了第{count}只烤鸭。")event.clear()if __name__ == '__main__':event=threading.Event()produce_duck_thread=threading.Thread(target=produce_duck, args=(event,));consume_duck_thread=threading.Thread(target=consume_duck, args=(event,));produce_duck_thread.start()consume_duck_thread.start()consume_duck_thread.join()consume_duck_thread.join()

    队列(queue)

    使用队列(queue)来实现生产者&消费者模型,具体需求:每次生产出一只烤鸭,就会被等待的消费者消费。技术要求:

    • 为了体现出队列先进先出和线程安全的特点,所以我们使用队列来存储烤鸭。
    • 烤鸭总数量作为所有生产者线程的共享变量,需要加锁。
    • 用多线程来实现这个需求。
    import queue
    import random
    import threading
    import time#烤鸭总数量(共享变量)
    duck_count=0
    #为了保证duck_count线程安全,需要加锁。
    duck_count_lock=threading.Lock()
    #把生产的烤鸭放入队列
    duck_queue=queue.Queue()def produce_duck(duck_queue,produce_id,max_duck_count):""":param duck_queue: 方烤鸭的队列:param produce_id: 生产者id:param max_duck_count: 所有生产者最多生产多少烤鸭。:return:"""global duck_count#生产者总是不停地生产烤鸭。while True:with duck_count_lock:if duck_count>=max_duck_count:#如果不在队列中加入None,则主线程不会停止,因为消费者无法退出。duck_queue.put(None)breakduck_count+=1duck_num=duck_countduck_queue.put(duck_num)print(f"生产者{produce_id}生产了{duck_num}只烤鸭。")def consume_duck(duck_queue,consume_id):#消费者不停地消费while True:duck_num=duck_queue.get()if duck_num is None:breakprint(f"消费者{consume_id}消费了第{duck_num}只烤鸭。")#标记任务完成duck_queue.task_done()if __name__ == '__main__':produce_duck_list=[threading.Thread(target=produce_duck,args=(duck_queue,i,10000))for i in range(1,5)]consume_duck_list = [threading.Thread(target=consume_duck, args=(duck_queue, i))for i in range(1, 3)]for produce_duck_thread in produce_duck_list:produce_duck_thread.start()for consume_duck_thread in consume_duck_list:consume_duck_thread.start()for consume_duck_thread in consume_duck_list:consume_duck_thread.join()for produce_duck_thread in produce_duck_list:produce_duck_thread.join()print("主线程结束。")

    相关文章:

    Python多线程知多少

    目录 目标 Python版本 官方文档 概述 线程 守护线程 线程同步 事件对象(Event Object) 实战 创建线程的基本语法 阻塞线程 守护线程 线程同步的方法 互斥锁(排他锁) 信号量(Semaphore) 事件…...

    C++ Qt常见面试题(8):C++ Qt中的线程同步与互斥

    在C++ Qt中,线程同步和互斥通常通过 QMutex 和 QMutexLocker 来实现。线程同步确保多个线程不会同时访问共享资源,而互斥机制通过锁定一个资源,确保在任何给定时刻只有一个线程能够访问它。 以下是一个使用 QMutex 来同步和互斥访问共享资源的详细示例代码: 1. 使用 QMut…...

    数字内容个性化推荐的关键是什么?

    智能算法交互体系构建 构建数字内容体验的智能推荐系统,本质上是实现数据驱动与算法响应的动态协同。其核心在于建立多维度用户数据与机器学习模型的深度交互链路——通过实时采集用户点击、停留时长、交互路径等行为特征,结合设备属性、场景状态等上下…...

    DeepSeek-OpenSourceWeek-第三天-Release of DeepGEMM

    DeepGEMM:这是一款专为高效的 FP8(8 位浮点)通用矩阵乘法(GEMMs)而开发的尖端库。GEMMs 是许多 AI 工作负载(尤其是深度学习)中的基本操作。 特点: 支持稠密和 MoE GEMMs:它可以处理标准的稠密矩阵乘法以及混合专家(MoE)模型中使用的矩阵乘法。MoE 是一种神经网络架…...

    LeetCode 1472.设计浏览器历史记录:一个数组完成模拟,单次操作均O(1)

    【LetMeFly】1472.设计浏览器历史记录:一个数组完成模拟,单次操作均O(1) 力扣题目链接:https://leetcode.cn/problems/design-browser-history/ 你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage &#xff0c…...

    AI+游戏,正在进行时!

    2月,DeepSeek引领的AI浪潮对游戏行业造成了巨大冲击。 2月17日马斯克在社交平台宣布,xAI将成立一家AI游戏工作室,高调宣布两大核心理念,打破大公司的垄断,利用AI重构游戏体验。随后的新闻中还表示,团队计划…...

    贪心算法精品题

    1.找钱问题 本题的贪心策略在于我们希望就可能的保留作用大的5元 class Solution { public:bool lemonadeChange(vector<int>& bills) {std::map<int ,int> _map;for(auto ch:bills){if(ch 5) _map[ch];else if(ch 10){if(_map[5] 0) return false;else{_m…...

    sql server 复制从备份初始化数据

    参考 &#xff1a; 从备份初始化订阅&#xff08;事务&#xff09; - SQL Server | Microsoft Learn sql server 复制默认是用快照初始化数据的&#xff0c;也支持从备份初始化数据&#xff0c;参考如上...

    【蓝桥杯】1.k倍区间

    前缀和 #include <iostream> using namespace std; const int N100010; long long a[N]; int cnt[N]; int main(){int n, m;cnt[0] 1;cin >> n >> m;long long res 0;for(int i 1; i < n; i){scanf("%d", &a[i]);a[i] a[i-1];res cnt…...

    Qt互斥锁(QMutex)的使用、QMutexLocker的使用

    Qt互斥锁【QMutex】的使用、QMutexLocker的使用 Chapter1 Qt互斥锁(QMutex)的使用、QMutexLocker的使用一、QMutexLocker和QMutex实现示例图二、QMutex和QMutexLocker的关系&#xff08;个人理解&#xff09;三、QMutex使用和QMutexLocker使用1.QMutex的使用2.QMutexLocker的使…...

    具身智能(Embodied AI)的物理交互基准测试:构建真实世界的智能体评估体系

    文章目录 引言:从虚拟到物理的智能跃迁一、具身智能测试体系设计1.1 评估维度矩阵1.2 测试平台技术栈二、核心测试场景构建2.1 基础运动能力测试集2.2 复杂操作任务设计三、物理仿真引擎关键技术3.1 高精度接触力学模型3.2 传感器噪声模拟四、评估指标体系4.1 量化指标公式4.2…...

    Javaweb后端数据库多表关系一对多,外键,一对一

    多表关系 一对多 多的表里&#xff0c;要有一表里的主键 外键 多的表上&#xff0c;添加外键 一对一 多对多 案例...

    鸿蒙 ArkUI 实现敲木鱼小游戏

    敲木鱼是一款具有禅意的趣味小游戏&#xff0c;本文将通过鸿蒙 ArkUI 框架的实现代码&#xff0c;逐步解析其核心技术点&#xff0c;包括动画驱动、状态管理、音效震动反馈等。 一、架构设计与工程搭建 1.1 项目结构解析 完整项目包含以下核心模块&#xff1a; ├── entry…...

    cv2.solvePnP 报错 求相机位姿

    目录 报错信息及解决&#xff1a; cv2.solvePnP 使用例子&#xff1a; 设置初始值效果也不好 cv2.projectPoints 函数效果不好 报错信息及解决&#xff1a; File "/shared_disk/users/lbg/project/human_4d/nlf_pose/render_demo_pkl2_cal.py", line 236, in <…...

    Linux实操——在服务器上直接从百度网盘下载(/上传)文件

    Linux Linux实操——在服务器上直接从百度网盘下载&#xff08;/上传&#xff09;文件 文章目录 Linux前言一、下载并安装bypy工具二、认证并授权网盘账号三、将所需文件转移至目的文件夹下四、下载文件五、上传文件六、更换绑定的百度云盘账户 前言 最近收到一批很大的数据&…...

    2004-2024年光刻机系统及性能研究领域国内外发展历史、差距、研究难点热点、进展突破及下一个十年研究热点方向2025.2.27

    一.光刻机概述 1.1 定义与原理 光刻机是 集成电路芯片制造的核心设备 ,其工作原理基于 光学成像和化学反应 。它通过 曝光系统 将掩模版上的图形精确地转移到涂覆于硅片表面的光刻胶上。这个过程涉及复杂的物理和化学反应,主要包括以下几个步骤: 涂胶 :在硅片表面均匀涂抹…...

    请求Geoserver的WTMS服务返回200不返回图片问题-跨域导致

    今天碰到个奇怪问题&#xff0c;改了个页面标题再打包布署GeoServer发现调用WTMS服务失败&#xff0c;请求返回状态码200&#xff0c;返回包大小0&#xff0c;使用postman模拟请求是可以正常返回图片的。 跟之前版本对比如下&#xff1a; 正常Response请求: HTTP/1.1 200X-Fr…...

    ubuntu配置jmeter

    1.前提准备 系统 ubuntu server 22.04 前提条件&#xff1a;服务器更新apt与安装lrzsz&#xff1a;更新apt&#xff1a; sudo apt update安装lrzsz: 命令行下的上传下载文件工具 sudo apt install lrzszsudo apt install zip2.安装jemeter 2.1.下载jdk17 输入命令&#xf…...

    《Qt动画编程实战:轻松实现头像旋转效果》

    《Qt动画编程实战&#xff1a;轻松实现头像旋转效果》 Qt 提供了丰富的动画框架&#xff0c;可以轻松实现各种平滑的动画效果。其中&#xff0c;旋转动画是一种常见的 UI 交互方式&#xff0c;广泛应用于加载指示器、按钮动画、场景变换等。本篇文章将详细介绍如何使用 Qt 实现…...

    【Mac电脑本地部署Deepseek-r1:详细教程与Openwebui配置指南】

    文章目录 前言电脑配置&#xff1a;安装的Deepseek版本&#xff1a;使用的UI框架&#xff1a;体验效果展示&#xff1a;本地部署体验总结 部署过程Ollama部署拉取模型运行模型Openwebui部署运行Ollama服务在Openwebui中配置ollama的服务 后话 前言 deepseek最近火的一塌糊涂&a…...

    黑马Mybatis

    Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

    通过Wrangler CLI在worker中创建数据库和表

    官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

    镜像里切换为普通用户

    如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

    Linux云原生安全:零信任架构与机密计算

    Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

    linux 下常用变更-8

    1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

    微信小程序云开发平台MySQL的连接方式

    注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

    Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

    引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

    Mac下Android Studio扫描根目录卡死问题记录

    环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

    打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

    一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

    「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

    在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...