进程
进程
Python的多进程编程是充分利用多核CPU资源、提高程序执行效率的重要手段。以下是对Python进程和多进程的详细解析,涵盖基本概念、核心原理、代码示例和应用场景。
进程的基本概念
- 进程:操作系统分配资源的基本单位。每个进程有独立的内存空间,互不干扰。
- 多进程:通过创建多个进程并行执行任务,充分利用多核CPU的计算能力。适合处理CPU密集型任务(如科学计算、图像处理)。多进程通过应用程序利用计算机的多核资源达到同时执行多个任务的目的,以此来提升程序的执行效率。
多进程的核心原理
multiprocessing模块
Python通过multiprocessing模块实现多进程,其核心原理包括:
- 独立内存空间:每个进程拥有独立的内存,避免共享资源冲突。
- 进程间通信:通过Queue、Pipe等工具实现数据交换。
- 进程管理:父进程可创建、启动、终止子进程,并监控其状态。
代码示例
单独进程
#!/usr/bin/env python
from multiprocessing import Process
import os
def hello(name):
#获取进程id
print("process {}".format(os.getpid()))
print('hello ' + name)
hello('world')
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi.py
process 1319
hello world
子进程
#!/usr/bin/env python
from multiprocessing import Process
import os
def hello(name):
#获取进程id
print("process {}".format(os.getpid()))
print('hello ' + name)
#main()函数就运行在主进程中
def main():
print("process {}".format(os.getpid()))
#Process对象是一个子任务,运行时会自动创建一个子进程,参数args是一个元组
p = Process(target=hello,args=('world',))
print('child process start')
#开启子进程
p.start()
#阻塞主进程,直到子进程结束
p.join()
print('child process stop')
if __name__ == '__main__':
main()
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi.py
process 1349
child process start
process 1350
hello world
child process stop
多进程
首先程序以单进程运行
#/usr/bin/env python
import time
def io_task():
#挂起当前进程1秒
time.sleep(1)
def main():
start_time = time.time()
for i in range(5):
io_task()
end_time = time.time()
print("程序运行耗时:{:.2f}".format(end_time - start_time))
if __name__ == '__main__':
main()
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python single_process.py
程序运行耗时:5.00
程序以多进程运行
#/usr/bin/env python
from multiprocessing import Process
import time
def io_task():
#挂起当前进程1秒
time.sleep(1)
def main():
start_time = time.time()
#创建一个进程列表存放进程
process_list = []
for i in range(5):
process_list.append(Process(target=io_task))
for p in process_list:
p.start()
for p in process_list:
p.join()
end_time = time.time()
print("程序运行耗时:{:.2f}".format(end_time - start_time))
if __name__ == '__main__':
main()
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_process.py
程序运行耗时:1.01
程序执行效率提升了将近5倍。
拓展一个知识点,我们在写一些程序的时候需要获取计算机的cpu数量。
Python 3.12.7 (main, Nov 8 2024, 17:55:36) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from multiprocessing import cpu_count
>>> cpu_count()
4
>>>
进程间通信
Pipe:管道
代码示例
#!/usr/bin/env python
from multiprocessing import Process,Pipe
#创建管道两端。一端发送,一端接收
conn1,conn2 = Pipe()
def send():
data = "Hello World"
conn1.send(data)
print("send data:{}".format(data))
def recv():
data = conn2.recv()
print("receive data:{}".format(data))
def main():
process_list = []
p1 = Process(target=send)
p2 = Process(target=recv)
process_list.append(p1)
process_list.append(p2)
for p in process_list:
p.start()
for p in process_list:
p.join()
if __name__ == '__main__':
main()
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_pipe.py
send data:Hello World
receive data:Hello World
Queue:队列
代码示例
#!/usr/bin/env python
from multiprocessing import Process,Queue
q = Queue()
def send(q):
data = "Hello World"
q.put(data)
print("send data:{}".format(data))
def recv(q):
data = q.get()
print("receive data:{}".format(data))
def main():
process_list = []
p1 = Process(target=send,args=(q,))
p2 = Process(target=recv,args=(q,))
process_list.append(p1)
process_list.append(p2)
for p in process_list:
p.start()
for p in process_list:
p.join()
if __name__ == '__main__':
main()
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_queue.py
send data:Hello World
receive data:Hello World
进程锁
在Python多进程和多线程中,当我们需要对多进程和多线程的共享资源或对象进行修改操作时,往往会出现因为cpu随机调度的问题而导致结果和我们的预期不一致的情况。这时候就需要对进程或者线程进行加锁操作,以保证一个进程或者一个线程在对共享资源或者对象进行修改时,其它的进程或者线程无法访问这个资源或者对象,直到获取锁的进程或者线程操作执行完毕后释放锁。因此,锁在多进程和多线程中起到了一个同步的作用,以保证每个进程和线程必要操作的完整执行。
错误示例
#!/usr/bin/env python
from multiprocessing import Process,Value
import time
#该函数运行在子进程中,参数val是一个Value对象,返回的是一个共享的全局变量
def func(val):
for i in range(50):
#挂起当前进程0.1秒,避免程序运行过快,造成cpu过载
time.sleep(0.1)
val.value += 1
if __name__ == '__main__':
val = Value('i',0)
#创建十个进程
process_list = [Process(target=func,args=(val,)) for i in range(10)]
for p in process_list:
p.start()
for p in process_list:
p.join()
print(val.value)
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
364
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
385
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
332
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
384
可以看见每次运行结果不一致,而且正确结果应为500
正确示例
#!/usr/bin/env python
from multiprocessing import Process,Value,Lock
import time
#该函数运行在子进程中,参数val是一个Value对象,返回的是一个共享的全局变量
#正确的做法是每次进行+1操作之前为i加上一把锁
def func(val,lock):
for i in range(50):
#挂起当前进程0.1秒,避免程序运行过快,造成cpu过载
time.sleep(0.1)
#加锁
lock.acquire()
val.value += 1
#释放锁
lock.release()
if __name__ == '__main__':
val = Value('i',0)
lock = Lock()
#创建十个进程
process_list = [Process(target=func,args=(val,lock)) for i in range(10)]
for p in process_list:
p.start()
for p in process_list:
p.join()
print(val.value)
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
500
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
500
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_value.py
500
进程锁牺牲了效率提高了安全性!
进程池
进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再次放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲的进程才能继续执行,也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行,这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。
代码示例
#!/usr/bin/env python
from multiprocessing import Pool
import os,time,random
#任务在子进程中运行
def task(name):
#获取进程id
print('process {} 开启运行,id:{}'.format(name,os.getpid()))
start_time = time.time()
#假设有个比较费时的操作
#生成一个随机数
time.sleep(random.random()*3)
end_time = time.time()
print('任务{}结束运行,耗时:{:.2f}'.format(name,end_time - start_time))
if __name__ == '__main__':
print('当前是主进程,id:{}'.format(os.getpid()))
p = Pool(4)
for i in range(1,6):
#以异步的方式启动进程池
#池中的进程随机接收任务,直到全部任务完成
p.apply_async(task,args=(i,))
#关闭进程池
p.close()
print('开始运行子进程')
p.join()
print('======================')
print('子进程运行完毕,当前是主进程,id:{}'.format(os.getpid()))
运行结果
┌──(root㉿kali)-[~/python_code/python_4]
└─# python multi_pool.py
当前是主进程,id:2120
开始运行子进程
process 1 开启运行,id:2121
process 2 开启运行,id:2122
process 3 开启运行,id:2123
process 4 开启运行,id:2124
任务1结束运行,耗时:0.50
process 5 开启运行,id:2121
任务5结束运行,耗时:1.74
任务2结束运行,耗时:2.31
任务3结束运行,耗时:2.36
任务4结束运行,耗时:2.99
======================
子进程运行完毕,当前是主进程,id:2120
浙公网安备 33010602011771号