1. 进程和线程
多线程
基于多线程对上述串行示例进行优化:
- 一个工厂,创建一个车间,这个车间中创建 3 个工人,并行处理任务。
- 一个程序,创建一个进程,这个进程中创建 3 个线程,并行处理任务。
串行下载示例:
python
import time
import requests
url_list = [
(
"东北4楼的秀.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f57000bvbmace0gvch7lo53oog",
),
(
"卡特扣盛.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34qlg",
),
(
"罗斯mvp.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuers5aa4tjj4gy6ajgg",
),
]
for file_name, url in url_list:
res = requests.get(url)
with open(file_name, mode="wb") as f:
f.write(res.content)使用线程优化后的示例:
python
import time
import requests
import threading
url_list = [
(
"东北4楼的秀.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f57000bvbmace0gvch7lo53oog",
),
(
"卡特扣盛.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34qlg",
),
(
"罗斯mvp.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuers5aa4tjj4gy6ajgg",
),
]
def task(file_name, video_url):
res = requests.get(video_url)
with open(file_name, mode="wb") as f:
f.write(res.content)
print(time.time())
print(time.time())
for name, url in url_list:
# 创建线程,让每个线程都去执行 task 函数(参数不同)
t = threading.Thread(target=task, args=(name, url))
# 线程启动
t.start()多进程
基于多进程对上述串行示例进行优化:
- 一个工厂,创建三个车间,每个车间一个工人(共3人),并行处理任务。
- 一个程序,创建三个进程,每个进程一个线程(共3人),并行处理任务。
进程创建之后,在进程中自动创建线程
python
import time
import requests
import multiprocessing
url_list = [
(
"东北4楼的秀.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f57000bvbmace0gvch7lo53oog",
),
(
"卡特扣盛.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34qlg",
),
(
"罗斯mvp.mp4",
"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuers5aa4tjj4gy6ajgg",
),
]
def task(file_name, video_url):
res = requests.get(video_url)
with open(file_name, mode="wb") as f:
f.write(res.content)
print(time.time())
if __name__ == "__main__":
print(time.time())
for name, url in url_list:
# 创建进程,让每个进程都去执行 task 函数(参数不同)
t = multiprocessing.Process(target=task, args=(name, url))
# 进程启动
t.start()综上所述,多进程的开销比多线程的开销大。那是不是使用多线程要比多进程更好?肯定不是
接下来,给大家再来介绍一个 Python 内置的 GIL(全局解释器锁)的知识,然后再根据进程和线程各自的特点总结各自适合的应用场景。
GIL(全局解释器锁)
GIL(Global Interpreter Lock)是 Python 解释器的一个机制,让一个进程中同一时刻只有一个线程可以被CPU调用。这意味着在 CPU 密集型任务中,多线程无法充分利用多核 CPU的优势,因为 GIL 会导致线程间的切换和竞争,从而影响性能。
如果程序想利用计算机的多核优势,让CPU同时处理一些任务,适合用多进程开发(即使资源开销大)
如果程序不利用计算机的多核优势,适合用多线程开发(即使资源开销小)
适用场景总结
常见的程序开发中,计算操作需要使用CPU多核优势,IO操作不需要使用CPU多核优势。
- 计算密集型,用多进程,例如:大量的数据计算(累加计算示例)
- IO 密集型,用多线程,例如:文件读写、网络数据传输(下载抖音短视频)
累加计算示例:
串行处理
python
import time
start = time.time()
# 相加到一亿
result = 0
for i in range(100000000):
result += i
print(result)
end = time.time()
print("耗时:", end - start) # 示例机器约 9.52s多进程处理(分段求和示例)
python
import time
import multiprocessing
def task(start, end, queue):
result = 0
for i in range(start, end):
result += i
queue.put(result)
if __name__ == "__main__":
queue = multiprocessing.Queue()
start_time = time.time()
# 每一个进程处理五千万个数的累加
p1 = multiprocessing.Process(target=task, args=(0, 50000000, queue))
p1.start()
p2 = multiprocessing.Process(target=task, args=(50000000, 100000000, queue))
p2.start()
v1 = queue.get(block=True)
v2 = queue.get(block=True)
print(v1 + v2)
end_time = time.time()
print("耗时:", end_time - start_time) # 示例机器约 2.62s在某些场景下,可以把多进程和多线程结合使用:例如创建 2 个进程(建议与 CPU 个数相同),每个进程内部创建 3 个线程来处理 IO 任务或辅助任务。
python
import multiprocessing
import threading
def thread_task():
pass
def task(start, end):
t1 = threading.Thread(target=thread_task)
t1.start()
t2 = threading.Thread(target=thread_task)
t2.start()
t3 = threading.Thread(target=thread_task)
t3.start()
if __name__ == "__main__":
p1 = multiprocessing.Process(target=task, args=(0, 50000000))
p1.start()
p2 = multiprocessing.Process(target=task, args=(50000000, 100000000))
p2.start()