百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Python多线程:让程序 “多线作战” 的秘密武器

moboyou 2025-06-07 16:55 17 浏览


一、什么是多线程?

在日常生活中,我们可以一边听音乐一边浏览新闻,这就是 “多任务处理”。在Python编程里,多线程同样允许程序同时执行多个任务,从而提升程序的执行效率和响应速度 。不过,Python中的多线程由于全局解释器锁(GIL)的存在,在CPU密集型任务中表现受限,但在I/O密集型任务(如网络请求、文件读写)中却能大放异彩。

二、Python多线程核心函数及案例

1. threading.Thread()

功能:用于创建线程对象,是多线程编程的基础。通过传入target参数指定线程要执行的函数,args参数传递函数所需的参数。

案例:同时打印两条不同的消息,模拟多任务执行。

import threading
import time

def print_message(message):
    for _ in range(3):
        print(message)
        time.sleep(1)

thread1 = threading.Thread(target=print_message, args=("线程1正在工作",))
thread2 = threading.Thread(target=print_message, args=("线程2正在工作",))

thread1.start()
thread2.start()

thread1.join()
thread2.join()
print('主程序执行结束')


#程序执行后打印如下:
线程1正在工作
线程2正在工作
线程1正在工作
线程2正在工作
线程1正在工作
线程2正在工作
主程序执行结束

#多次执行后可能如下:
线程1正在工作
线程2正在工作
线程1正在工作线程2正在工作

线程1正在工作
线程2正在工作
主程序执行结束

解析:定义print_message函数用于打印消息,创建thread1和thread2两个线程对象,分别传入不同的消息作为参数。调用start()方法启动子线程,线程开始并行执行对应的函数;join()方法会阻塞主线程(主程序执行时就算一个主线程),等待子线程执行完毕。

以上结果因为添加了join函数,主程序最后的print打印消息是在最后才执行,但是子线程打印消息还是出现了跟预期不一致的地方。如果注释掉上面的join方法,多次执行结果可能如下:会发现主程序的print日志混在了子线程print日志中间,原因是因为主线程和子线程同时操作print函数导致的。

#结果1
线程1正在工作
线程2正在工作
主程序执行结束
线程1正在工作线程2正在工作

线程2正在工作
线程1正在工作

#结果2
线程1正在工作
线程2正在工作
主程序执行结束
线程1正在工作
线程2正在工作
线程1正在工作线程2正在工作

2. threading.Lock()

功能:创建锁对象,用于解决多线程中共享资源的竞争问题,避免数据不一致。当一个线程获取锁后,其他线程必须等待,直到该线程释放锁。

案例:接着上面的案例,我们通过对print打印加锁,保证同一时间只有一个线程操作print打印。

通过threading.Lock()定义锁对象,通过lock.acquire()加锁,lock.release()释放锁。


import threading
import time

lock = threading.Lock()
def print_message(message):
    for _ in range(3):
        lock.acquire()
        print(message)
        lock.release()
        time.sleep(1)

thread1 = threading.Thread(target=print_message, args=("线程1正在工作",))
thread2 = threading.Thread(target=print_message, args=("线程2正在工作",))

thread1.start()
thread2.start()

thread1.join()
thread2.join()
print('主程序执行结束')

#程序执行后,结果如下:不会出现打印错乱的情况
线程1正在工作
线程2正在工作
线程1正在工作
线程2正在工作
线程1正在工作
线程2正在工作
主程序执行结束

3. threading.Timer()

功能:创建一个定时器线程,在指定的延迟时间后执行函数。

案例:延迟3秒后打印提示信息。

import threading

def delayed_function(name):
    print(f"3秒已到,{name}开始执行任务!")
#定时三秒
timer = threading.Timer(3, delayed_function,args=('线程1',))

timer.start()

解析:threading.Timer(3, delayed_function)表示创建一个延迟 3 秒后执行delayed_function的定时器线程,调用start()启动定时器。

4. threading.current_thread()

功能:返回当前正在执行的线程对象。

案例:在函数中查看当前执行的线程。

import threading
def check_thread():
    current = threading.current_thread()
    print(f"当前执行的线程是: {current.name}")
#子线程执行
thread = threading.Thread(target=check_thread)
thread.start()
#主程序执行
check_thread()

#结果:
当前执行的线程是: Thread-1 (check_thread)
当前执行的线程是: MainThread

解析:check_thread函数中使用threading.current_thread()获取当前线程对象,并打印线程名称,既在子线程中调用,也在主线程中调用,对比输出结果。

5. threading.active_count()

功能:返回当前活动的线程数量(包括主线程)。

案例:统计程序中活动线程数。

import threading
import time


def thread_task():
    time.sleep(2)

threads = []
for _ in range(3):
    t = threading.Thread(target=thread_task)
    threads.append(t)
    t.start()

print(f"启动子线程后,活动线程数: {threading.active_count()}")

for t in threads:
    t.join()

print(f"子线程执行完毕后,活动线程数: {threading.active_count()}")

#执行结果
启动子线程后,活动线程数: 4
子线程执行完毕后,活动线程数: 1

解析:创建3个子线程并启动,使用threading.active_count()分别在启动子线程后和子线程执行完毕后统计活动线程数量,子线程结束后主线程还在,所以最后还统计到1个。

6.threading.local()

功能:threading.local用于创建线程局部变量,每个线程都有自己独立的变量副本,线程之间的变量互不干扰。

案例:模拟多线程处理用户请求,每个线程记录自己处理的请求编号。

import threading

# 创建threading.local对象
local_data = threading.local()


def process_request(request_id):
    # 为每个线程设置独立的请求编号
    local_data.request_id = request_id
    print(f"线程 {threading.current_thread().name} 正在处理请求 {local_data.request_id}")
    # 在同一线程的其他函数中使用该变量
    additional_processing()


def additional_processing():
    print(f"线程 {threading.current_thread().name} 继续处理请求 {local_data.request_id}")


threads = []
for i in range(3):
    t = threading.Thread(target=process_request, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

#结果:
线程 Thread-1 (process_request) 正在处理请求 0
线程 Thread-1 (process_request) 继续处理请求 0
线程 Thread-2 (process_request) 正在处理请求 1
线程 Thread-2 (process_request) 继续处理请求 1
线程 Thread-3 (process_request) 正在处理请求 2
线程 Thread-3 (process_request) 继续处理请求 2

解析:首先创threading.locallocal_data。process_request函数中,为每个线程设置独立request_id属性,不同线程local_data.request_id相互独立。additional_processing函数可以在同一线程内访问该属性,体现了线程局部变量在同一线程内共享、不同线程间隔离的特性。

三、实用案例详解

1. 多线程处理网络请求

场景:需要从多个网站获取数据,如果顺序执行,会浪费大量等待响应的时间。使用多线程可以同时发起多个请求,提高效率。

import requests
import threading


def fetch_data(url):
    try:
        response = requests.get(url)
        print(f"从 {url} 获取数据成功,状态码: {response.status_code}")
    except requests.RequestException as e:
        print(f"从 {url} 获取数据失败: {e}")


urls = [
    "https://www.example1.com",
    "https://www.example2.com",
    "https://www.example3.com"
]

threads = []
for url in urls:
    t = threading.Thread(target=fetch_data, args=(url,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

解析:定义fetch_data函数用于发送网络请求并处理响应,将多个 URL 放入列表,为每个 URL 创建一个线程发起请求,实现多任务并行处理网络请求。

2. 多线程实现文件读写

场景:同时读取多个文件并进行处理,或者同时向多个文件写入数据。

import threading

def read_file(file_path):
    try:
        with open(file_path, 'r') as file:
            content = file.read()
            print(f"读取 {file_path} 内容: {content}")
    except FileNotFoundError:
        print(f"{file_path} 不存在")


file_paths = ["file1.txt", "file2.txt", "file3.txt"]
threads = []
for path in file_paths:
    t = threading.Thread(target=read_file, args=(path,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

解析:read_file函数负责读取文件内容,通过多线程为每个文件路径创建线程,同时读取多个文件,提升文件处理效率。

4、总结

Python多线程是提升程序效率的有力工具,尤其在I/O密集型任务中优势显著。通过掌握threading模块的核心函数,灵活运用多线程技术,让程序实现 “多线作战”。但也要牢记多线程的局限性,不是线程越多越好,同时避免死锁,线程安全问题,最后选择合适的并发方案,才能发挥出 Python的最大潜力!

相关推荐

免费主机|永久免费空间|php虚拟主机|博客主机|论坛主机|免费域名

免费主机|永久免费空间|php虚拟主机|博客主机|论坛主机|免费域名|云主机在出教程之前准备好久,测试搭建轻量论坛无压力选用稳定免费域名免费主机分销给,可以套CDN使用坚持免费时间是大厂不能媲美,刚开...

.NET和Blazor WebAssembly 轻量级博客

简介Blogifier是一个用ASP编写的自托管开源发布平台。.NET和BlazorWebAssembly。它可以用来快速,轻松地建立一个轻量级的,但功能齐全的个人或团体博客。截图支持md教程如果...

等了30年,微软MS-DOS神器重生:用Rust重写、开源斩获9.9k Star、还能跑在Linux上!

整理|苏宓出品|CSDN(ID:CSDNnews)曾经称“开源是毒瘤”时有多么嫌弃,现在“微软开源”就有多么热烈,甚至舍得把很多经典的系统、项目都逐步开源出来。回看过去两年间,微软先是开源...

教程 | 一文搭建你的第一个免费专属博客

我建了一个QQ学习交流群,旨在“分享、讨论、学习、资源分享、就业机会、互联网内推、共同进步!”,感兴趣的可以加一下,也可以添加我的QQ~QQ群:1002821945;QQ号:498073774;前言...

YzmCMS是一款基于YZMPHP开发的一套轻量级开源内容管理系统

YzmCMS是一款基于YZMPHP开发的一套轻量级开源内容管理系统,YzmCMS简洁、安全、开源、实用,可运行在Linux、Windows、MacOSX、Solaris等各种平台上,专注为公司企业、个...

PyPoster, 轻量级的博客发布小工具

引言PyPoster是一个采用Python3.5编写的博客离线发布小工具,GUI采用tkinter框架构建。理论上,可以在安装了Python运行环境的多种平台下使用它。PyPoster目前...

Java和前端哪个更累?(java与前端哪个更推荐)

一、首先前后端开发各是什么?1.前端开发:网站的“前端”是与用户直接交互的部分,包括你在浏览网页时接触的所有视觉内容--从字体到颜色,以及下拉菜单和侧边栏。这些视觉内容,都是由浏览器解析、处理、渲染相...

Linux系统区别英文字母大小写(linux的命令是否区分大小写)

我们一般在Windows系统开发程序并进行功能测试,如果上线的时候选择Windows服务器的话,是什么问题都没有。但是当选择Linux系统的时候,就必须注意Linux系统是严格的区别文字大小。Wind...

原创:带你全面了解和学习PHP(php学的是什么)

PHP能做什么?学习PHP,你应该感到幸运,因为如果你学过其他语言,你就会发现PHP还是相对简单的,如果是初学阶段,你要搞清楚HTML和PHP的概念,之后你完全可以让PHP给你算算一加一等于几,然后在...

我把 Mac mini 托管到机房了:一套打败云服务器的终极方案

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:薯仔不爱吃薯仔我把我积灰的Macmini托管到机房了,有图有真相。虽然画质又渣又昏暗,但是!这就是实锤。作为开发者,谁不想拥有个自己的服...

PHP技能评测(php认证考试)

公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...

PHP的相似性和差异Ruby ON Rails,Python

就像我们所说的语言是唯一的不同,编程语言也有变化,从知名度、可用性和可靠性。每一种语言都有不同方面的用途。之间的主要相似PHP,RubyonRails和Python是他们都是动态的面向对象的语言。...

查看WordPress站点查询缓慢问题并进行优化教程

大家都知道WordPress是个需要大量查询的程序,查询越多,WordPress网站越慢,如何优化WordPress查询呢?这里我们需要用到QueryMonitor插件,也就是查询监视器插件。在本教...

go 和 php 性能如何进行对比?(go php7 对比)

PHP性能很差吗?每次讲到PHP和其他语言间的性能对比,似乎都会发现这样一个声音:单纯的性能对比没有意义,主要瓶颈首先是数据库,其次是业务代码等等。好像PHP的性能真的不能单独拿出来讨论似的。但其实一...

PHP在做爬虫时的解决方案(php实现爬虫)

爬虫不是一个小众的场景,所以无论是哪个语言,都有很多相应的生态库.这里介绍一下PHP的技术方案和代码量。关键能力对页面的解析能力PHP的官方扩展中有Dom扩展,但是我建议使用electrolinux/...