Python3并发编程详解

Python3并发编程详解 日常开发中批量任务、接口请求、文件读写、爬虫抓取、数据计算等场景串行执行效率低下并发编程就是解决执行速度瓶颈的核心方案。一、并发基础概念1. 核心名词区分串行任务挨个依次执行前一个结束才运行下一个效率最低并发同一时间段交替处理多个任务宏观同时运行微观交替切换并行同一时刻多核CPU同时执行多个任务真正同时运行线程轻量级执行单元依附进程存在共享进程资源切换开销小进程资源独立隔离拥有独立内存空间切换开销大协程用户态轻量级任务代码层面手动切换无内核切换损耗2. GIL全局解释器锁Python独有锁机制同一时刻一个进程内仅有一个线程执行限制多线程无法利用多核CPUCPU密集型任务多线程效率低适用IO密集型网络请求、文件读写、休眠等待多线程优势明显3. 任务场景选型IO密集型优先多线程 / 协程CPU计算密集型优先多进程高吞吐异步请求优先asyncio协程二、多线程编程 threading模块1. 线程创建两种方式方式一函数式创建线程importthreadingimporttimedeftask(name,delay):print(f线程{name}开始执行休眠{delay}秒)time.sleep(delay)print(f线程{name}执行完毕)if__name____main__:# 创建线程对象t1threading.Thread(targettask,args(一号,2))t2threading.Thread(targettask,args(二号,1))# 启动线程t1.start()t2.start()# 等待线程执行结束t1.join()t2.join()print(所有线程运行结束)方式二类继承创建线程importthreadingimporttimeclassMyThread(threading.Thread):def__init__(self,name,delay):super().__init__()self.namename self.delaydelaydefrun(self):print(f线程{self.name}开始执行休眠{self.delay}秒)time.sleep(self.delay)print(f线程{self.name}执行完毕)if__name____main__:t1MyThread(线程A,1)t2MyThread(线程B,2)t1.start()t2.start()t1.join()t2.join()2. 线程守护守护线程随主线程退出直接销毁非守护线程执行完毕程序才结束# daemonTrue 设置为守护线程tthreading.Thread(targettask,daemonTrue)3. 线程安全与互斥锁多线程共享全局变量并发修改会出现数据错乱使用Lock加锁保证原子性importthreading num0lockthreading.Lock()defadd_count():globalnum# 上锁lock.acquire()for_inrange(100000):num1# 释放锁lock.release()if__name____main__:t1threading.Thread(targetadd_count)t2threading.Thread(targetadd_count)t1.start()t2.start()t1.join()t2.join()print(最终统计值,num)4. 线程池 ThreadPoolExecutor避免无限创建线程资源耗尽池化复用线程管控并发数量fromconcurrent.futuresimportThreadPoolExecutorimporttimedefwork(num):print(f任务{num}执行中)time.sleep(1)returnf任务{num}完成if__name____main__:# 设置最大并发线程数withThreadPoolExecutor(max_work3)aspool:task_list[pool.submit(work,i)foriinrange(6)]# 遍历获取执行结果forresintask_list:print(res.result())三、多进程编程 multiprocessing模块1. 进程基础创建进程资源相互独立默认不共享数据规避GIL锁限制适合计算密集型importmultiprocessingimporttimedefprocess_task(name):print(f进程{name}启动运行)time.sleep(2)print(f进程{name}运行结束)if__name____main__:p1multiprocessing.Process(targetprocess_task,args(P1,))p2multiprocessing.Process(targetprocess_task,args(P2,))p1.start()p2.start()p1.join()p2.join()print(全部进程执行完毕)2. 进程间数据通信使用队列Queue实现进程安全数据传递frommultiprocessingimportProcess,Queuedefput_data(q):q.put(进程传递数据)defget_data(q):print(接收数据,q.get())if__name____main__:qQueue()p1Process(targetput_data,args(q,))p2Process(targetget_data,args(q,))p1.start()p2.start()p1.join()p2.join()3. 进程池 ProcessPoolExecutor批量管理进程充分利用多核CPU算力fromconcurrent.futuresimportProcessPoolExecutorimportmathdefcalc_power(num):returnmath.pow(num,3)if__name____main__:withProcessPoolExecutor(max_work4)aspool:resultpool.map(calc_power,[1,2,3,4,5])print(list(result))进程线程核心区别内存进程独立内存线程共享进程内存开销进程创建销毁开销大线程开销更小GIL多进程突破GIL限制线程受GIL约束通信进程通信复杂线程共享变量通信简单四、协程编程 asyncio1. 协程基础概念单线程内切换任务无系统内核切换消耗IO阻塞时自动切换其他任务并发效率最高关键字async定义协程函数await阻塞挂起任务2. 基础协程写法importasyncio# 定义协程函数asyncdefdemo1():print(协程任务1开始)awaitasyncio.sleep(2)print(协程任务1结束)asyncdefdemo2():print(协程任务2开始)awaitasyncio.sleep(1)print(协程任务2结束)asyncdefmain():# 创建并发任务t1asyncio.create_task(demo1())t2asyncio.create_task(demo2())awaitt1awaitt2# 运行协程入口asyncio.run(main())3. 批量并发任务importasyncioasyncdeftask_func(num):print(f异步任务{num}执行)awaitasyncio.sleep(1)returnf任务{num}完成asyncdefmain():tasks[task_func(i)foriinrange(5)]# 批量等待所有任务执行完毕res_listawaitasyncio.gather(*tasks)print(res_list)asyncio.run(main())4. 异步文件读写适配协程风格文件操作不阻塞异步流程importaiofilesasyncdefread_file():asyncwithaiofiles.open(test.txt,r,encodingutf-8)asf:contentawaitf.read()print(content)asyncio.run(read_file())五、并发常见问题与解决方案1. 死锁问题多个线程互相持有对方所需锁互相等待卡死解决统一加锁顺序、减少嵌套锁、设置超时释放2. 线程池/进程池参数设置根据CPU核心数、IO阻塞程度合理设定最大并发数避免阻塞雪崩3. 并发异常捕获批量任务中单独捕获异常单个任务报错不影响整体流程4. 三种并发选型总结并发方式适用场景优缺点多线程IO密集、网络请求、文件读写开销小受GIL限制无法多核计算多进程CPU密集、大规模数值计算利用多核资源开销大通信繁琐协程高并发IO、爬虫、异步接口效率最高仅单线程运行语法特殊全文总结本文详细介绍了串行 / 并发 / 并行、GIL 锁基础理论覆盖多线程、多进程、协程三大并发实现方案。