Python开发——Python 线程入门

An Intro to Threading in Python – Real Python        

1. 什么是线程?

        线程是一个独立的执行流程。这意味着您的程序将有两件事情同时发生。但对于大多数 Python 3 实现来说,不同的线程实际上并不是同时执行的:它们只是看起来是这样。

        人们很容易把线程想象成程序上运行着两个(或多个)不同的处理器,每个处理器同时执行一项独立的任务。这几乎是对的。线程可能运行在不同的处理器上,但它们一次只能运行一个。

        要同时运行多个任务,需要使用非标准的 Python 实现,用不同的语言编写部分代码,或者使用多进程,而多进程会带来一些额外的开销。

        由于 CPython 实现 Python 的工作方式,线程可能无法加快所有任务的速度。这是由于与 GIL 的交互作用,基本上每次只能运行一个 Python 线程。

        大部分时间都在等待外部事件的任务一般都适合使用线程。而那些需要大量 CPU 计算且等待外部事件的时间很少的问题,运行速度可能根本不会快。

        这适用于用 Python 编写并在标准 CPython 实现上运行的代码。如果您的线程是用 C 语言编写的,它们可以释放 GIL 并同时运行。如果您在不同的 Python 实现上运行,请查阅文档,了解它是如何处理线程的。

        如果您运行的是标准 Python 实现,只用 Python 编写,并且有 CPU 限制的问题,那么您应该选择多处理模块。

        将程序架构为使用线程也能提高设计的清晰度。您将在本教程中学到的大多数示例并不一定会因为使用了线程而运行得更快。在这些示例中使用线程有助于使设计更简洁、更易于推理。

        所以,让我们停止谈论线程,开始使用它吧!

 2. 开始线程

        现在您已经知道了线程是什么,让我们来学习如何创建一个线程。Python 标准库提供了线程,它包含了你在本文中将看到的大多数基元。本模块中的 Thread 很好地封装了线程,提供了一个简洁的接口来使用它们。

        要启动一个单独的线程,需要创建一个线程实例,然后告诉它 .start():

import loggingimport threadingimport timedef thread_function(name):logging.info("Thread %s: starting", name)time.sleep(2)logging.info("Thread %s: finishing", name)if __name__ == "__main__":format = "%(asctime)s: %(message)s"logging.basicConfig(format=format, level=logging.INFO,datefmt="%H:%M:%S")logging.info("Main    : before creating thread")x = threading.Thread(target=thread_function, args=(1,))logging.info("Main    : before running thread")x.start()logging.info("Main    : wait for the thread to finish")x.join()logging.info("Main    : all done")

        如果查看一下日志语句,就会发现主要部分是创建和启动线程:

x = threading.Thread(target=thread_function, args=(1,))x.start()

        创建线程时,你会向它传递一个函数和一个包含该函数参数的列表。在本例中,你告诉线程运行 thread_function(),并将 1 作为参数传递给它。

        本文将使用顺序整数作为线程的名称。threading.get_ident()可以为每个线程返回一个唯一的名称,但这些名称通常既不简短也不易读。

        thread_function() 本身并没有做什么。它只是简单地记录一些信息,并在信息之间加入 time.sleep()。

        运行该程序(注释掉第 20 行)时,输出结果如下:

$ ./single_thread.pyMain    : before creating threadMain    : before running threadThread 1: startingMain    : wait for the thread to finishMain    : all doneThread 1: finishing

        你会注意到,线程在代码的 Main 部分结束后才结束。在下一节中,我们将回过头来讨论为什么会这样,并谈谈神秘的第 20 行。

3. 守护进程线程

        在计算机科学中,守护进程是在后台运行的进程。

        Python 线程对守护进程有更具体的含义。守护线程会在程序退出时立即关闭。思考这些定义的一种方法是,将守护线程视为一个在后台运行的线程,而不必担心关闭它。

        如果程序运行的线程不是守护进程,那么程序会等待这些线程完成后才终止。而作为守护进程的线程则会在程序退出时被杀死。

        让我们再仔细看看上面程序的输出结果。最后两行是最有趣的部分。当你运行程序时,你会发现在 __main__ 打印出 all done 消息后,在线程结束前有一个停顿(约 2 秒)。

        这个停顿是 Python 在等待非调用线程完成。当 Python 程序结束时,关闭过程的一部分就是清理线程例程。

        如果您查看 Python 线程的源代码,就会发现 threading._shutdown() 会遍历所有正在运行的线程,并对每一个没有设置守护进程标志的线程调用 .join()。

        因此,程序等待退出是因为线程本身处于休眠状态。一旦完成并打印了信息,.join() 就会返回,程序就可以退出了。

        通常,这种行为是你想要的,但我们还有其他选择。首先,让我们用守护进程线程重复该程序。方法是改变线程的构造,添加 daemon=True 标志:

x = threading.Thread(target=thread_function, args=(1,), daemon=True)

        现在运行程序,应该会看到以下输出:

$ ./daemon_thread.pyMain    : before creating threadMain    : before running threadThread 1: startingMain    : wait for the thread to finishMain    : all done

        这里的区别在于输出的最后一行不见了。 thread_function() 没有机会完成。它是一个守护线程,所以当 __main__ 的代码结束,程序想要结束时,守护线程就被杀死了。

4. 线程的使用场景

        线程非常适合处理以下类型的任务:

        1. I/O 密集型任务:例如文件读写、网络请求等。这类任务通常涉及大量的等待时间,线程可以在等待期间执行其他任务,从而提高效率。

        2. 后台任务:例如日志记录、监控系统状态、处理定时任务等。这类任务通常不需要立即的响应,可以在后台异步处理。

5. 高级线程操作

5.1 使用线程池

        在处理大量线程时,直接管理每个线程可能会变得复杂。此时,使用线程池(ThreadPoolExecutor)可以简化管理。

from concurrent.futures import ThreadPoolExecutorimport loggingimport threadingimport timedef thread_function(name):logging.info("Thread %s: starting", name)time.sleep(2)logging.info("Thread %s: finishing", name)if __name__ == "__main__":format = "%(asctime)s: %(message)s"logging.basicConfig(format=format, level=logging.INFO,datefmt="%H:%M:%S")logging.info("Main    : before creating thread pool")with ThreadPoolExecutor(max_workers=3) as executor:for i in range(5):executor.submit(thread_function, i)logging.info("Main    : all done")

        在这个例子中,ThreadPoolExecutor 创建了一个包含最多 3 个工作线程的线程池,并提交了 5 个任务。线程池会自动管理线程的生命周期和任务调度。

5.2 线程同步

        在线程之间共享数据时,需要注意线程安全问题。Python 提供了多种同步机制,例如锁(Lock)、信号量(Semaphore)和条件变量(Condition)。

        锁(Lock)

import threadinglock = threading.Lock()shared_resource = 0def thread_safe_increment():global shared_resourcewith lock:shared_resource += 1threads = []for _ in range(5):t = threading.Thread(target=thread_safe_increment)threads.append(t)t.start()for t in threads:t.join()print(shared_resource)

        信号量(Semaphore)

        信号量适用于需要限制同时访问资源的线程数量的场景。

import threadingimport timesemaphore = threading.Semaphore(3)def access_resource(name):with semaphore:print(f"{name} is accessing the resource")time.sleep(2)print(f"{name} is releasing the resource")threads = []for i in range(5):t = threading.Thread(target=access_resource, args=(f"Thread-{i}",))threads.append(t)t.start()for t in threads:t.join()

5.3 线程间通信

        Python 提供了队列(Queue)来实现线程之间的安全通信。

import threadingimport queueimport timeq = queue.Queue()def producer():for i in range(5):print(f"Producing {i}")q.put(i)time.sleep(1)def consumer():while True:item = q.get()if item is None:breakprint(f"Consuming {item}")time.sleep(2)t1 = threading.Thread(target=producer)t2 = threading.Thread(target=consumer)t1.start()t2.start()t1.join()q.put(None)t2.join()

        在这个例子中,生产者线程将数据放入队列,而消费者线程从队列中取出数据进行处理。

6. 多线程的注意事项

        1. 避免死锁:当多个线程互相等待对方释放资源时,会导致程序卡死。

        2. GIL 的限制:由于 Python 的全局解释器锁(GIL),多线程并不能真正并行执行 CPU 密集型任务。可以考虑使用多进程来绕过这个限制。

        3. 资源清理:确保在程序结束时正确关闭和清理线程。

7. 结论

        通过以上的介绍和示例,相信您已经对 Python 中的线程有了更深入的理解。线程在处理 I/O 密集型任务和后台任务时非常有用,但在使用时需要注意线程同步和通信等问题,以避免线程安全问题。

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

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

相关文章

Vue3中的jsx的babel配置

如果我们希望在项目中使用jsx,那么我们需要添加对jsx的支持: jsx我们通常会通过Babel来进行转换(React编写的jsx就是通过babel转换的);对于Vue来说,我们只需要在Babel中配置对应的插件即可; *…

Vue+Xterm.js+WebSocket+JSch实现Web Shell终端

一、需求 在系统中使用Web Shell连接集群的登录节点 二、实现 前端使用Vue&#xff0c;WebSocket实现前后端通信&#xff0c;后端使用JSch ssh通讯包。 1. 前端核心代码 <template><div class"shell-container"><div id"shell"/>&l…

C++ 实现字符串逆序

C 实现字符串逆序 思路&#xff1a; 输入一个字符串。使用双指针法&#xff0c;交换字符串的首尾字符&#xff0c;逐步向中间移动。输出逆序后的字符串。 #include <iostream> #include <string>using namespace std;void reverseString(string &str) {int …

【FPGA】STA静态时序分析

文章目录 一.定义二.分类1. 静态时序分析2. 静态时序分析 三. 概念四. 时间余量1.场景2.建立时间余量3.保持时间余量 一.定义 时序分析:检查电路是否满足时序要求&#xff1b; 二.分类 1. 静态时序分析 STA,遍历所有的时序路径&#xff0c;根据时序库&#xff08;.lib文件&…

【Mojolicious RESTful接口全解】构建现代化Web服务的秘诀

标题&#xff1a;【Mojolicious RESTful接口全解】构建现代化Web服务的秘诀 Mojolicious是一个基于Perl的高性能、实时的Web框架&#xff0c;它以其简洁的语法和强大的功能而闻名。Mojolicious不仅支持传统的Web应用开发&#xff0c;还特别适合构建RESTful API。本文将详细介绍…

新手教学系列——使用uWSGI对Flask应用提速

在构建和部署Flask应用时,性能和稳定性是两个关键的因素。为了提升Flask应用的性能,我们可以借助uWSGI这个强大的工具。本文将详细介绍为什么要使用uWSGI、uWSGI的底层原理,并提供一个实例配置,帮助你更好地理解和应用这个工具。 为什么要使用uWSGI uWSGI 是一个应用服务…

探索企业知识边界,鸿翼ECM AI助手开启智慧问答新时代

在信息化迅速发展的当下&#xff0c;企业积累的数字文档数量巨大&#xff0c;这些文档中蕴含的深层信息对业务发展至关重要。然而&#xff0c;传统的搜索技术常常因只能进行关键字查询而无法满足对文档深层次理解的需求。 据Gartner调查&#xff0c;高达47%的员工在寻找有效工…

Webpack: 三种Chunk产物的打包逻辑

概述 在前文 Webpack: Dependency Graph 管理模块间依赖 中&#xff0c;我们已经详细讲解了「构建」阶段如何从 Entry 开始逐步递归读入、解析模块内容&#xff0c;并最终构建出模块依赖关系图 —— ModuleGraph 对象。本文我们继续往下&#xff0c;讲解在接下来的「封装」阶段…

【大数据】—美国交通事故分析(2016 年 2 月至 2020 年 12 月)

引言 在当今快速发展的数字时代&#xff0c;大数据已成为我们理解世界、做出决策的重要工具。特别是在交通安全领域&#xff0c;大数据分析能够揭示事故模式、识别风险因素&#xff0c;并帮助制定预防措施&#xff0c;从而挽救生命。本文将深入探讨2016年2月至2020年12月期间&…

【redis】 LRU 和 LFU 算法

1、简介 Redis 中的 LRU&#xff08;Least Recently Used&#xff09;和 LFU&#xff08;Least Frequently Used&#xff09;算法是用于决定在内存空间不足时&#xff0c;哪些键&#xff08;key&#xff09;应该被删除以释放空间的策略。这两种算法都试图通过跟踪键的使用情况…

解决Memcached内存碎片:优化缓存性能的策略

解决Memcached内存碎片&#xff1a;优化缓存性能的策略 Memcached是一个广泛使用的高性能分布式内存缓存系统&#xff0c;它通过在内存中缓存数据来加速数据检索操作。然而&#xff0c;随着时间的推移和缓存操作的进行&#xff0c;Memcached可能会遇到内存碎片问题&#xff0c…

24年河南特岗教师招聘流程+报名流程

河南特岗教师报名流程如下 1.登录河南省特岗招聘网 登录河南省特岗招聘网注册账号和密码&#xff0c;账号可以是手机号或者身份证号&#xff0c;密码自己设置 2.注册登录账号 注册完账号重新登录账号&#xff0c;输入身份证号、手机号、密码、验证码 3.浏览考试须知 填写个人信…

Python 编程快速上手——让繁琐工作自动化(第2版)读书笔记01 Python基础快速过关

Python 编程快速上手——让繁琐工作自动化&#xff08;第2版&#xff09;读书笔记01 Python基础快速过关 1 python基础概念 Python提供了高效的高级数据结构&#xff0c;还能简单有效地面向对象编程。 python运算符顺序 **——%——//——/——*——-——python中常见的数据…

Real-Time 3D Graphics with WebGL2

WebGL渲染管线 下图是WebGL渲染管线的示意图: Vertex Buffer Objects (VBOs) VBOS中包含了用于描述几何体的信息。如&#xff0c;几何体的顶点坐标&#xff0c;法线坐标&#xff0c;颜色&#xff0c;纹理坐标等。 Index Buffer Objects (IBOs) IBOs中包含了描述顶点关系的信…

C#的多线程UI窗体控件显示方案 - 开源研究系列文章

上次编写了《LUAgent服务器端工具》这个应用&#xff0c;然后里面需要新启动一个线程去对文件进行上传到FTP服务器&#xff0c;但是新线程里无法对应用主线程UI的内容进行更改&#xff0c;所以就需要在线程里设置主UI线程里控件信息的方法&#xff0c;于是就有了此博文。此文记…

Rocky Linux 9 快速安装docker 教程

前述 CentOS 7系统将于2024年06月30日停止维护服务。CentOS官方不再提供CentOS 及后续版本&#xff0c;不再支持新的软件和补丁更新。CentOS用户现有业务随时面临宕机和安全风险&#xff0c;并无法确保及时恢复。由于 CentOS Stream 相对不稳定&#xff0c;刚好在寻找平替系统…

idm 支持断点续传吗 idm 断点续传如何使用 idm断点续传怎么解决 idm下载中断后无法继续下载

断点续传功能&#xff0c;让我再也不会惧怕下载大型文件。在断点续传的帮助下&#xff0c;用户可以随时暂停下载任务&#xff0c;并在空闲时继续之前的下载进程。下载文件不惧网络波动&#xff0c;断点续传让下载过程更稳定。有关 idm 支持断点续传吗&#xff0c;idm 断点续传如…

JavaScript:if-else类型

目录 任务描述 相关知识 if语句 if-else语句 匹配问题 编程要求 任务描述 本关任务&#xff1a;根据成绩判断考试结果。 相关知识 在编程中&#xff0c;我们常常根据变量是否满足某个条件来执行不同的语句。 JavaScript中利用以if关键字开头的条件语句达到以上目的&am…

商城项目回顾

哈哈&#xff0c;准备期末考试去了&#xff0c;项目停了一段时间。现在又忘的差不多了。所以专门写一篇博客总结前期项目的知识点。 Client软件包 代码加总结&#xff1a; 这段代码实现了一个简单的客户端程序&#xff0c;用于与服务器建立连接、发送登录信息并接收服务器的响…