Python技术迷

Python中的线程和进程有什么区别?

昨天晚上加班到十点多,回家路上在地铁上刷群,结果看到有人问“Python里的线程和进程到底有啥区别啊?”。我当时手机快没电了,顺手回了两句,但想想,这事其实不少人都老是搞混,今天就随便聊聊,顺便加点代码,大家爱看不看哈。

先说个事,我上周在公司茶水间碰到小李,他那台老电脑死活跑不起来多任务,一顿抱怨。我说你到底搞的是多线程还是多进程啊?结果他说:“不是都一样吗?”——卧槽,这可真不是一个东西啊!

进程就像是你电脑里开的每个软件,比如你开了微信、开了VSCode、又开了Chrome,每一个都是独立的进程。互相之间老死不相往来,互相影响不了。

线程呢,其实就是进程里干活的“工人”。你比如说你用Chrome开了十个网页,每个网页其实可能都是一个线程在跑。线程之间能沟通,能抢资源,毕竟都在一个进程的锅里吃饭。

Python里,线程和进程为啥都能用?

Python比较特殊的点就是,虽然都能开多线程,但有个GIL(全局解释器锁),让你多线程其实没那么香。你看这段代码:

import threading

defworker():
for i in range(3):
        print(f"线程干活:{i}")

threads = []
for i in range(2):
    t = threading.Thread(target=worker)
    t.start()
    threads.append(t)

for t in threads:
    t.join()

这段代码开了两个线程,每个线程都在print。但你要是跑计算型的活,比如大数据计算,这两个线程会被GIL限制,实际速度其实没有多快。有点像两个壮汉被绳子绑一起,轮流抡锤子——效率也高不到哪去。

多进程才是真并行

你要是真想并行,那还得用多进程。再举个栗子,昨天我在家炖汤的时候顺便写了个小脚本,用multiprocessing模块:

from multiprocessing import Process

defcook():
for i in range(3):
        print(f"进程炖菜:{i}")

ps = []
for i in range(2):
    p = Process(target=cook)
    p.start()
    ps.append(p)

for p in ps:
    p.join()

这个和多线程语法差不多,但每个Process都直接是一个新进程,完全独立。比如你开两个进程,实际上操作系统底层都是真实的多核去跑的。像我那破电脑,CPU风扇转得都快飞起来。

线程适合I/O,进程适合计算密集

有人问,那到底啥时候用线程,啥时候用进程?这个其实挺简单:

  • 线程:适合I/O型的活,比如爬虫、下载文件、网络请求。因为GIL其实对I/O没啥影响,反正你等网络的时候CPU空着也是空着。
  • 进程:适合计算密集,比如做AI运算、图像处理那种,纯CPU吃紧的活,直接多进程安排上,能用满所有CPU核心。

我那天帮朋友改爬虫,他开了几十个线程,网速不行,CPU根本没啥压力,线程用得很爽。但上回帮老板做大文件批量加密,直接用多进程,否则一台16核机器跟单核没区别。

线程数据能共享,进程不行

还有个容易踩坑的地方,线程之间能共享内存、变量,直接全局变量说用就用。进程可不一样,数据天生隔离,你要通信还得用队列、管道、共享内存这些花活。

比如下面这个例子,线程共享数据:

import threading

num = 0

defadd():
global num
for _ in range(10000):
        num += 1

t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
print(num)  # 理论上是20000,但可能不是,注意线程安全

如果用多进程,上面这个num各玩各的,根本不会冲突,也不会叠加。

进程开销大,线程轻量

还有就是,多进程开销会大一点,毕竟每个进程系统都要给一套资源。线程就省事得多,一个进程里随便造十个八个线程都无所谓。你要是只处理几十上百个小任务,线程就够了。

说实话,Python里线程和进程真的得看场景选。前两天在公司加班,发现有同事用线程去做CPU密集型工作,搞得又慢又卡,老板看了都懵逼。换成多进程秒提速。反过来,爬网页还用多进程,结果一堆内存泄露,机器直接卡死,真服了。

反正你就记住一条:

  • I/O多用线程,计算多用进程。
  • 线程数据能共享,进程天然隔离。
  • 线程受GIL限制,进程没有GIL。
  • 多进程真并行,线程其实是“伪”并行。

别问我怎么踩的坑,都是泪啊。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领