Python多进程编程实战:从单线程阻塞到并行处理
本文深入解析Python中的多进程概念,通过对比单线程顺序执行与多进程并行执行的耗时差异,演示如何使用os.fork()及multiprocessing.Process实现任务并行化。文章涵盖进程创建、父进程与子进程返回值的区别,以及通过join等待子进程完成的具体代码示例,帮助开发者理解如何利用多核CPU提升程序执行效率。
现代操作系统均具备“多任务”处理能力,在操作系统视角下,每一个运行的程序实例被称为一个进程(process)。例如,启动浏览器即意味着创建并运行了一个浏览器进程。
若将计算机的核心CPU比作一座工厂,那么进程就如同工厂内的车间,代表CPU当前处理的一个独立任务。在任意特定时刻,CPU仅能执行一个进程,其余进程则处于非运行状态(如就绪或阻塞状态)。
读者可能会产生疑惑:既然CPU同一时刻只运行一个进程,为何我们在浏览网页时,后台的音乐播放器依然能正常播放声音?
这实际上是操作系统采用时间片轮转机制的结果。操作系统让各个任务交替执行:任务1执行0.01秒后切换至任务2,任务2执行0.01秒后再切换至任务3,如此循环往复。由于CPU的处理速度极快,这种快速的交替执行给用户造成了所有任务“同时”运行的错觉。
Python中的多进程实现
在UNIX或Linux操作系统中,可以通过调用fork()函数来创建新进程。与普通函数调用一次返回一次不同,fork()函数调用一次却返回两次:操作系统会自动复制当前进程(称为父进程)生成一个新进程(称为子进程),随后分别在父进程和子进程的上下文中返回。
在fork()的返回结果中,子进程始终返回0,而父进程则返回新创建子进程的ID。这样设计的理由是,一个父进程可以fork出多个子进程,因此父进程需要记录每个子进程的ID以便管理;而子进程只需调用getppid()即可获取其父进程的ID。
fork()函数被封装在Python的os模块中。下面通过具体示例,对比使用多进程与不使用多进程在性能上的区别:
from random import randint
from time import time, sleep
def download_task(filename):
print('开始下载%s...' % filename)
time_to_download = randint(5, 10)
sleep(time_to_download)
print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))
def main():
start = time()
download_task('MySQL从删库到跑路.pdf')
download_task('万万没想到.mp4')
end = time()
print('总共耗费了%.2f秒.' % (end - start))if __name__ == '__main__':
main()
执行结果如下:
开始下载MySQL从删库到跑路.pdf...
MySQL从删库到跑路.pdf下载完成! 耗费了9秒
开始下载万万没想到.mp4...
万万没想到.mp4下载完成! 耗费了9秒
总共耗费了18.00秒.
从上述示例可见,若代码仅按顺序依次执行,即使两个下载任务互不相关,也必须等待第一个文件下载完成后才能开始第二个任务。这种方式显然缺乏效率且不合理。接下来,我们将使用多进程方式将两个下载任务分配到不同的进程中并行执行,代码如下:
from multiprocessing import Process
from os import getpid
from random import randint
from time import time, sleep
def download_task(filename):
print('启动下载进程,进程号[%d].' % getpid()) print('开始下载%s...' % filename) time_to_download = randint(5, 10)
sleep(time_to_download)
print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))
def main():
start = time()
p1 = Process(target=download_task, args=('MySQL从删库到跑路.pdf', ))
p1.start()
p2 = Process(target=download_task, args=('万万没想到.mp4', ))
p2.start()
p1.join()
p2.join()
end = time()
print('总共耗费了%.2f秒.' % (end - start))
if __name__ == '__main__': main()
多进程执行结果如下:
启动下载进程,进程号[568408].
开始下载万万没想到.mp4...万万没想到.
mp4下载完成! 耗费了6秒
启动下载进程,进程号[565896].
开始下载MySQL从删库到跑路.
pdf...MySQL从删库到跑路.pdf下载完成! 耗费了10秒
总共耗费了10.09秒.
运行上述代码可以明显观察到,两个下载任务几乎是“同时”启动的。由于并行处理,程序的总执行时间大幅缩短,不再等于两个任务耗时的简单相加,而是接近耗时较长的那个任务的时间。
以上就是Python多进程学习笔记的详细内容,更多关于Python多进程的高级用法与资料,请关注本站其他相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













