当前位置:首页 > 技术 > 正文内容

深入理解Python多线程编程

访客 技术 2026年5月29日 1

线程基础

线程是程序中的一个执行单元,能够同时运行多个线程可以使程序完成更多任务。但在标准Python解释器中,多个线程并非真正意义上的并行执行,而是通过轮转调度实现的。

对于需要长时间等待外部事件的任务(如IO操作),使用线程可以有效提升程序效率。但对于计算密集型任务,线程可能不会带来性能提升。

顺序执行与多线程对比

默认情况下,Python代码是顺序执行的。以下是一个顺序执行的示例:

import time

start_time = time.time()
print('1. 洗壶:1min')
time.sleep(1)
print('2. 灌凉水:1min')
time.sleep(1)
print('3. 烧水:1min')
time.sleep(1)
print('4. 等水烧开:3min')
time.sleep(3)
print('5. 洗茶杯:1min')
time.sleep(1)
print('6. 放茶叶:1min')
time.sleep(1)
print('7. 泡茶:1min')
time.sleep(1)
print(f"总运行时间:{time.time() - start_time:.2f}s")

多线程实现异步操作

通过threading模块可以实现多线程。以下是一个改写后的示例:

import time
import threading

def prepare_tea():
    print("5. 洗茶杯:1min")
    time.sleep(1)
    print("6. 放茶叶:1min")
    time.sleep(1)

start_time = time.time()
print("1. 洗壶:1min")
time.sleep(1)
print("2. 灌凉水:1min")
time.sleep(1)
print("3. 烧水:1min")
time.sleep(1)
print("4. 等水烧开:3min")
time.sleep(3)

tea_thread = threading.Thread(target=prepare_tea)
tea_thread.start()

print("7. 泡茶:1min")
time.sleep(1)

print(f"总运行时间:{time.time() - start_time:.2f}s")

线程创建与管理

Python的threading模块提供了简便的线程管理接口。

基本线程创建

import threading

def download_file():
    print("开始下载文件...")
    time.sleep(1)
    print("完成下载文件...")

def upload_file():
    print("开始上传文件...")
    time.sleep(1)
    print("完成上传文件...")

# 创建并启动线程
download_thread = threading.Thread(target=download_file)
upload_thread = threading.Thread(target=upload_file)
download_thread.start()
upload_thread.start()

守护线程

守护线程会在主线程退出时自动终止。

import threading
import time

def upload_file():
    print("开始上传文件...")
    time.sleep(1)
    print("完成上传文件...")

def download_file():
    print("开始下载文件...")
    time.sleep(1)
    print("完成下载文件...")

if __name__ == '__main__':
    upload_thread = threading.Thread(target=upload_file)
    download_thread = threading.Thread(target=download_file)
    upload_thread.setDaemon(True)
    download_thread.setDaemon(True)
    upload_thread.start()
    download_thread.start()

    print("主线程结束")

线程安全与竞争条件

当多个线程操作共享资源时,可能会引发竞争条件。

竞争条件示例

import threading

count = 0
lock = threading.Lock()

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

if __name__ == '__main__':
    threads = []
    for _ in range(2):
        thread = threading.Thread(target=increment)
        threads.append(thread)
        thread.start()
    
    for thread in threads:
        thread.join()
    
    print(f"最终计数:{count}")

死锁与锁管理

避免死锁可以通过合理使用锁和避免无限等待实现。

锁的基本用法

import threading

lock = threading.Lock()

def synchronized(func):
    def wrapper(*args, **kwargs):
        lock.acquire()
        try:
            return func(*args, **kwargs)
        finally:
            lock.release()
    return wrapper

@synchronized
def critical_section():
    print("执行关键代码...")
    time.sleep(1)

thread1 = threading.Thread(target=critical_section)
thread2 = threading.Thread(target=critical_section)
thread1.start()
thread2.start()

死锁示例

import threading

lock = threading.Lock()

def deadlock():
    lock.acquire()
    print("等待另一个线程释放锁...")
    # 这里会发生死锁,因为两个线程都在等待对方释放锁
    lock.acquire()

dead_thread = threading.Thread(target=deadlock)
dead_thread.start()
dead_thread.join()

总结

  • 线程优势:能够同时执行多个任务,特别适合IO密集型操作
  • 线程劣势:存在竞争条件和死锁风险,需要合理使用锁机制
  • 性能注意事项:对于计算密集型任务,可能需要考虑使用multiprocessing模块
标签: Python多线程

相关文章

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。