当前位置:首页>python>带你彻底搞懂Python的多线程与多进程,别再搞混了

带你彻底搞懂Python的多线程与多进程,别再搞混了

  • 2026-09-10 13:50:59
带你彻底搞懂Python的多线程与多进程,别再搞混了

很多初学者学Python,一碰到多线程和多进程就头大。我也经历过这个阶段,总觉得这两个东西差不多,用起来又老出问题。今天我用最直白的话,帮你把这事彻底理清。

先说核心区别。多线程是在一个程序里同时干几件事,多进程是同时开几个程序干活。举个例子,你一个人在厨房做饭,这就是一个程序。你想一边烧水一边切菜,这叫多线程。你要是喊来另一个人,他负责切菜你负责烧水,这叫多进程。

Python的多线程有个大坑,叫GIL锁。这个东西让很多新手吃了亏。简单说,GIL是一把锁,它规定同一时刻只能有一个线程在工作。那你可能会问,多线程还有什么用?对于I/O密集型任务,也就是读写文件、网络请求这类需要等待的操作,多线程确实能提高效率。因为一个线程在等数据的时候,GIL会释放,另一个线程就可以干活。但对于CPU密集型任务,比如循环计算、图像处理,多线程反而比单线程慢。

多进程就不一样了。每个进程都有自己的GIL,没有那个锁的限制。多进程可以真正利用多核CPU,适合处理CPU密集型任务。但代价也明显,进程之间的通信比线程复杂,内存开销也大很多。

怎么选?我跟你讲个真实例子。我有个朋友写爬虫,用多线程抓网页。程序跑起来,看起来在并发,实际上大部分时间都在等网络响应。这种情况下多线程很合适,代码简单,效率也高。后来他处理抓到的数据,要做大量计算。改用多进程后,速度直接翻了好几倍。

具体怎么用,看代码最直接。

import threading 

import time

def working(name):

   print(f'{name}开始工作')

   time.sleep(2)

   print(f'{name}工作结束')

thread1 = threading.Thread(target=working, args=('线程1',))

thread2 = threading.Thread(target=working, args=('线程2',))

thread1.start()

thread2.start()

thread1.join()

thread2.join()

这是最基本的多线程写法。启动两个线程,让他们同时干活。注意那个sleep,模拟的是等待操作。如果是计算任务,你改成循环算一千遍乘法,两个线程加一起的速度反而可能不如一个线程。

多进程的写法也类似,只是换成Process对象。

from multiprocessing import Process 

import os

def计算任务(n):

   result = sum(ii for i in range(n))

   print(f'进程{os.getpid()}计算结果:{result}')

if __name__ == '__main__':

   p1 = Process(target=计算任务, args=(10000000,))

   p2 = Process(target=计算任务, args=(10000000,))

   p1.start()

   p2.start()

   p1.join()

   p2.join()

这段代码里,两个进程分别算自己的,互不干扰。你可以试试在任务管理器里看,CPU占用会明显高起来。

还有一个常见误解,以为多线程能随便共享变量。线程之间确实能共享全局变量,但容易出现数据混乱。比如两个线程同时给同一个变量加1,结果可能只加了1次。解决办法是用锁,但用锁又会降低并发性能。多进程没有这个问题,因为进程之间内存是隔离的,想共享数据需要用Queue或者Pipe。

我见过最典型的错误是,新手在循环里创建几千个线程。每个线程就干一点小事,结果创建线程的开销比干活本身还大。正确的做法是用线程池或进程池,控制并发数量。

from concurrent.futures import ThreadPoolExecutor 

def下载文件(url):

   print(f'正在下载{url}')

   return '完成'

urls = ['url1', 'url2', 'url3']

with ThreadPoolExecutor(max_workers=3) as executor:

   results = list(executor.map(下载文件, urls))

看到没,max_workers设成3,最多就3个线程同时运行。这样既不会浪费资源,也不会出问题。

最后说个实在的建议。如果你不确定该用哪个,先想想你的程序主要在等什么。等I/O就选多线程,等CPU就算就选多进程。实在拿不准,用多进程准没错。虽然代码写起来复杂点,但不容易掉坑里。

我刚开始也把这两个混着用,出过不少bug。后来想明白了,其实就是一句话的事:多线程适合等,多进程适合算。记住这句话,以后看见别人写代码,你一眼就能看出他有没有用对。

最新文章

随机文章