Py学习  »  Python

30天入门Python(基础篇)——第14天:进程和线程

码上编程学习社 • 8 月前 • 429 次点击  

资料已经打包好了,需要的关注公众号发送“111”领取

“用代码下载10张图片,一张下完才开始下下一张,太慢了?”

“写的监控程序,既要采集数据又要显示结果,只能二选一?”

——之前写的代码都是“单线作战”,同一时间只干一件事。今天咱们学进程和线程,这两个“并行处理神器”能让代码同时执行多个任务,下载文件、监控系统、爬虫采集都能效率翻倍,这才是Python高手的干活方式。

一、先搞懂:进程和线程是什么?

用生活场景类比最容易理解:进程是“一个独立的工厂”,有自己的厂房和设备(系统资源);线程是“工厂里的工人”,共享厂房资源,同时干不同的活。比如打开微信(一个进程),里面的聊天、朋友圈、支付就是不同的线程。

核心关系:一个进程可以包含多个线程,线程共享进程的资源,切换速度比进程快;进程之间相互独立,资源不共享,稳定性更高。

二、多进程:让代码“开多个工厂”

多进程就是同时启动多个独立程序(工厂),适合CPU密集型任务(比如大量计算)。Python用multiprocessing模块实现多进程,跨平台兼容性好。

# 用多进程同时计算3组大数字的平方和from multiprocessing import Processimport time# 定义计算函数(CPU密集型任务)def calculate_square_sum(num_range):    start = time.time()    total = sum(x*x for x in num_range)    end = time.time()    print(f"计算结果:{total},耗时:{end-start:.2f}秒")if __name__ == "__main__":    # 3组需要计算的数字范围    ranges = [range(110000000), range(1000000020000000), range(2000000030000000)]    # 单进程执行(对比用)    print("单进程执行:")    start_single = time.time()    for r in ranges:        calculate_square_sum(r)    print(f"单进程总耗时:{time.time()-start_single:.2f}秒\n")    # 多进程执行    print("多进程执行:")    start_multi = time.time()    processes = []    # 创建3个进程    for r in ranges:        p = Process(target=calculate_square_sum, args=(r,))  # args传参数,必须是元组        processes.append(p)        p.start()  # 启动进程    # 等待所有进程完成    for p in processes:        p.join()    print(f"多进程总耗时:{time.time()-start_multi:.2f}秒")


运行后会发现,多进程总耗时不到单进程的1/3,效率提升明显。避坑点:Windows系统中,多进程代码必须放在if __name__ == "__main__"下,否则会报错。

三、多线程:让代码“多工人干活”

多线程是在一个进程里启动多个线程,适合IO密集型任务(比如下载文件、网络请求)。Python用threading模块实现多线程,比多进程更轻量。

# 用多线程同时下载3张图片(IO密集型任务)import threadingimport requestsimport time# 图片下载函数def download_image


    
(url, filename):    start = time.time()    response = requests.get(url)    with open(f"images/{filename}""wb"as f:        f.write(response.content)    end = time.time()    print(f"《{filename}》下载完成,耗时:{end-start:.2f}秒")if __name__ == "__main__":    # 确保图片文件夹存在    import os    os.makedirs("images", exist_ok=True)    # 3张图片的下载地址(替换为实际可用地址)    image_urls = [        ("https://xxx.com/img1.jpg""风景1.jpg"),        ("https://xxx.com/img2.jpg""风景2.jpg"),        ("https://xxx.com/img3.jpg""风景3.jpg")    ]    # 多线程执行    print("多线程下载开始:")    start = time.time()    threads = []    for url, name in image_urls:        t = threading.Thread(target=download_image, args=(url, name))        threads.append(t)        t.start()    # 等待所有线程完成    for t in threads:        t.join()    print(f"全部下载完成,总耗时:{time.time()-start:.2f}秒")


如果用单线程下载,总耗时是3个文件耗时之和;多线程则是“同时下载”,总耗时接近最慢的那一个文件,效率提升非常直观。

四、ThreadLocal:线程的“专属记事本”

线程共享进程资源,但有时候每个线程需要自己的“私有数据”(比如爬虫线程的cookie、用户线程的登录信息),用ThreadLocal就能给每个线程分配独立的存储空间,不用手动传递参数,代码更简洁。

# ThreadLocal的使用示例import threading# 创建ThreadLocal对象local_data = threading.local()# 线程执行函数def worker(worker_id):    # 给当前线程绑定私有数据    local_data.worker_id = worker_id    # 调用其他函数时,直接获取当前线程的私有数据    print_task()def print_task():    # 直接获取当前线程的worker_id,不用传参    print(f"线程{threading.current_thread().name}:工人编号{local_data.worker_id},开始工作")if __name__ == "__main__":    threads = []    for i in range(3):        t = threading.Thread(target=worker, args=(i+1,), name=f"WorkerThread-{i+1}")        threads.append(t)        t.start()    for t in threads:        t.join()


运行后会看到,每个线程都能正确获取自己的worker_id,互不干扰。ThreadLocal常用来处理Web开发中的用户会话、爬虫中的线程私有配置等场景。

五、进程 vs 线程:该选哪个?一张表说清

新手最纠结“什么时候用进程,什么时候用线程”,这张对比表直接抄作业:

对比维度

多进程

多线程

核心优势

稳定性高,适合CPU密集型任务

轻量快速,适合IO密集型任务

资源占用

高(独立资源)

低(共享资源)

切换速度

适用场景

大量计算(如数据分析)

下载、爬虫、网络请求

记住一个简单原则:计算多就用多进程,等待多就用多线程。

六、分布式进程:让多台电脑“一起干活”

如果一个任务太庞大,单台电脑的多进程/多线程也不够用,就需要“分布式进程”——让多台电脑同时执行任务,比如用10台电脑一起爬取一个大型网站的数据。Python用multiprocessing的managers模块实现分布式进程,核心是“主进程管理任务,子进程执行任务”。

# 简化版分布式进程:主进程发送任务,子进程执行# 1. 主进程(任务分发者)from multiprocessing.managers import BaseManagerimport queue# 创建任务队列和结果队列task_queue = queue.Queue()result_queue = queue.Queue()# 注册队列到网络,让子进程能访问class QueueManager(BaseManager):    passQueueManager.register('get_task_queue'callable=lambda: task_queue)QueueManager.register('get_result_queue'callable=lambda: result_queue)# 启动管理器,监听端口manager = QueueManager(address=(''5000), authkey=b'python30')manager.start()# 获取网络队列task = manager.get_task_queue()result = manager.get_result_queue()# 发送3个任务(计算数字平方)for i in [102030]:    print(f"发送任务:计算{i}的平方")    task.put(i)# 接收结果for _ in range(3):    r = result.get(timeout=10)    print(f"接收结果:{r}")# 关闭管理器manager.shutdown()# 2. 子进程(任务执行者,可在其他电脑运行)from multiprocessing.managers import BaseManager# 注册队列(和主进程一致)class QueueManager(BaseManager):    passQueueManager.register('get_task_queue')QueueManager.register('get_result_queue')# 连接主进程的管理器server_addr = '192.168.1.100'  # 主进程的IP地址manager = QueueManager(address=(server_addr, 5000), authkey=b'python30')manager.connect()# 获取队列并执行任务task = manager.get_task_queue()result = manager.get_result_queue()while True:    try:        n = task.get(timeout=1)        print(f"执行任务:计算{n}的平方")        result.put(f"{n}的平方是{n*n}")    except queue.Empty:        print("任务执行完毕")        break


把子进程代码放到多台电脑上运行,就能实现分布式计算,任务执行效率随电脑数量倍增。

今天的内容核心是“并行处理”:多进程和多线程解决单台电脑的效率问题,分布式进程解决多台电脑的协同问题。新手不用急于掌握分布式进程,先把多线程(处理IO任务)和多进程(处理计算任务)的基础用法练熟,日常开发就足够用了。记住,用对并行方式,代码效率能提升10倍甚至100倍。

【今日任务】用多线程实现“同时下载2张图片+播放提示音”的功能:① 下载图片用requests模块;② 播放提示音用winsound模块(Windows)或playsound模块(Mac);③ 确保下载和播放同时进行。

最后

以上就是今天给你分享的内容,觉得有用的话欢迎点赞收藏哦!

如果你也对Python这门编程感兴趣的话,欢迎加入我们。(小白也可以参加)

  Python零基础实战特训营 
三更老师亲自上课示范,2天:理论+实操教学+直播教学演示+课后辅导

获取方式:

1.关注下方公众号↓↓↓↓

2.点赞+再看

3.在后台发送:“python” 即可领取资料福利/开通上课权限

资料已经打包好了,需要的关注公众号发送“111”领取

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/190608