多进程操作
【一】multiprocessing模块介绍
-
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似
-
multiprocessing模块的功能众多:
-
- 支持子进程、通信和共享数据、执行不同形式的同步
- 提供了Process、Queue、Pipe、Lock等组件。
-
与线程不同,进程没有任何共享状,进程修改的数据,改动仅限于该进程内
【二】Process类的介绍
【1】创建进程的类
- 由该类实例化得到的对象,表示一个子进程中的任务
def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
*, daemon=None):
-
需要使用关键字的方式来指定参数
-
args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
【2】参数介绍
- group:参数未使用,值始终为None
- target:表示调用对象,即子进程要执行的任务
- args:表示调用对象的位置参数元组,args=(1,2,'ly',)
- kwargs:表示调用对象的字典,kwargs=
- name:子进程的名称
【3】方法介绍
p = multiprocessing.Process()
# p 是实例后的对象
-
p.start():
-
- 启动进程,并调用该子进程中的p.run()
-
p.run():
-
- 进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
-
p.terminate():
-
- 强制终止进程p,不会进行任何清理操作
- 如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
- 如果p还保存了一个锁那么也将不会被释放,进而导致死锁
-
p.is_alive():
-
- 如果p仍然运行,返回True
-
p.join([timeout]):
-
- 主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。
- timeout是可选的超时时间
- 需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
【4】属性介绍
-
p.daemon:
-
- 默认值为False
- 如果设为True,代表p为后台运行的守护进程
- 当p的父进程终止时,p也随之终止
- 并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
-
p.name:
-
- 进程的名称
-
p.pid:
-
- 进程的pid
-
p.exitcode:
-
- 进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
-
p.authkey:
-
- 进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
- 这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
【三】Process类的使用
【1】特别提醒
- 注意:在windows中Process()必须放到
if __name == 'main__':
下
Since Windows has no fork, the multiprocessing module starts a new Python process **and** imports the calling module.
If Process() gets called upon import, then this sets off an infinite succession of new processes (**or** until your machine runs out of resources).
This **is** the reason **for** hiding calls to Process() inside
**if name == "main"**
since statements inside this **if**-statement will **not** get called upon import.
- 由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块
- 如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)
- 这是隐藏对Process()内部调用的原,使用
if __name == “main__”
,这个if语句中的语句将不会在导入时被调用
【2】开设多线程的两种方式
(1)直接用Process方法
from multiprocessing import Process
import time
# 创建子进程程序
def work(name):
print(f"{name} is starting")
time.sleep(3)
print(f"{name} is ending")
def main():
# 创建子进程对象
# target 就是需要启动的子进程的函数名
# args 传入的位置参数,位置参数必须带 , 元组类型
p1 = Process(target=work, args=('drake',))
p2 = Process(target=work, args=('uzi',))
# 开启进程
p1.start()
p2.start()
# 一定是主进程先跑起来,再去启动子进程
if __name__ == "__main__":
main()
# drake is starting
# uzi is starting
# drake is ending
# uzi is ending
(2)继承Process类
from multiprocessing import Process
import time
# 创建一个新的类继承自Process
class MyProcess(Process):
def __init__(self, name):
super().__init__()
self.name = name
# 创建子进程程序
def work(self):
print(f"{self.name} is starting")
time.sleep(3)
print(f"{self.name} is ending")
def main():
# 创建子进程对象
p1 = MyProcess(name='drake')
p2 = MyProcess(name='uzi')
# 开启进程
p1.start()
p2.start()
# 一定是主进程先跑起来,再去启动子进程
if __name__ == "__main__":
start_time = time.time()
print(f"这是主进程 __main__ 开始\n")
main()
print(f"这是主进程 __main__ 结束\n")
end_time = time.time()
print(f'总耗时 :{end_time - start_time}s')
#这是主进程 __main__ 开始
#这是主进程 __main__ 结束
#总耗时 :0.05585050582885742s
# drake is starting
# uzi is starting
# drake is ending
# uzi is ending
【3】进程之间的内存空间是隔离的
import multiprocessing
# 多进程中的子进程之间的数据不共享
money = 9999
def change_money():
global money
print(f'这是原始的数据 :>>>> {money}')
# 在局部修改上面的 money 局部修改不可变数据类型
money = 8888
print(f'这是当前子进程修改后的数据 :>>>> {money}')
def main():
p = multiprocessing.Process(target=change_money, )
p.start()
print(f'这是start后的数据:>>>>{money}')
p.join()
print(f'这是join后的数据:>>>>{money}')
if __name__ == '__main__':
main()
# 这是start后的数据:>>>>9999
# 这是原始的数据 :>>>> 9999
# 这是当前子进程修改后的数据 :>>>> 8888
# 这是join后的数据:>>>>9999
【4】多线程实现服务端并发
- 服务端
#【1】实现并发
import multiprocessing
import socket
server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ip = '127.0.0.1'
port = 8080
ADDR = (ip, port)
server_socket.bind(ADDR)
server_socket.listen(5)
def run(conn):
while True:
data = conn.recv(1024)
print(f'这是来自客户端的消息:{data.decode()}')
conn.send(data.decode().encode())
def main():
while True:
conn, addr = server_socket.accept()
task = multiprocessing.Process(target=run, args=(conn,))
task.start()
if __name__ == '__main__':
main()
#【2】未实现并发
import multiprocessing
import socket
server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ip = '127.0.0.1'
port = 8080
ADDR = (ip, port)
server_socket.bind(ADDR)
server_socket.listen(5)
conn, addr = server_socket.accept()
while True:
data = conn.recv(1024)
print(f'这是来自客户端的消息:{data.decode()}')
conn.send(data.decode().encode())
- 客户端
import socket
client_socket = socket.socket()
ip = '127.0.0.1'
port = 8080
ADDR = (ip, port)
client_socket.connect(ADDR)
while True:
message = input("请输入发送给服务端的消息:").strip()
client_socket.send(message.encode())
data = client.recv(1024)
print(f"这是来自服务端的数据:{data.decode()}")
【5】Process对象的join方法
(1)非join方法的并行(主进程结束子进程未结束)
import multiprocessing
import time
def work(name):
print(f"{name} is starting")
time.sleep(3)
print(f"{name} is ending")
def main():
# 这个生成式在创建多个子进程
task_list = []
for i in range(1,4):
# target 就是需要启动的子进程的函数名
# args 传入的位置参数,位置参数必须带 , 元组类型
p = multiprocessing.Process(target=work, args=(i,))
task_list.append(p)
for task in task_list:
task.start()
if __name__ == '__main__':
start_time = time.time()
print(f"--- 主进程开始 ---")
main()
print(f"--- 主进程结束 ---")
end_time = time.time()
print(f'总耗时 :>>>> {end_time - start_time}s')
'''
--- 主进程开始 ---
--- 主进程结束 ---
总耗时 :>>>> 0.0717775821685791s
1 is starting
2 is starting
3 is starting
1 is ending
2 is ending
3 is ending
'''
(2)join方法的串行(主进程等待子进程结束)
import multiprocessing
import time
def work(name):
print(f"{name} is starting")
time.sleep(3)
print(f"{name} is ending")
def main():
# 这个生成式在创建多个子进程
task_list = []
for i in range(1,4):
# target 就是需要启动的子进程的函数名
# args 传入的位置参数,位置参数必须带 , 元组类型
p = multiprocessing.Process(target=work, args=(i,))
task_list.append(p)
for task in task_list:
task.start()
task.join()
if __name__ == '__main__':
start_time = time.time()
print(f"--- 主进程开始 ---")
main()
print(f"--- 主进程结束 ---")
end_time = time.time()
print(f'总耗时 :>>>> {end_time - start_time}s')
'''
--- 主进程开始 ---
1 is starting
1 is ending
2 is starting
2 is ending
3 is starting
3 is ending
--- 主进程结束 ---
总耗时 :>>>> 9.682202577590942s
'''
(3)join方法的并行(主进程等待子进程结束)
import multiprocessing
import time
def work(name):
print(f"{name} is starting")
time.sleep(3)
print(f"{name} is ending")
def main():
# 这个生成式在创建多个子进程
task_list = []
for i in range(1,4):
# target 就是需要启动的子进程的函数名
# args 传入的位置参数,位置参数必须带 , 元组类型
p = multiprocessing.Process(target=work, args=(i,))
task_list.append(p)
p.start()
for task in task_list:
task.join()
if __name__ == '__main__':
start_time = time.time()
print(f"--- 主进程开始 ---")
main()
print(f"--- 主进程结束 ---")
end_time = time.time()
print(f'总耗时 :>>>> {end_time - start_time}s')
'''
--- 主进程开始 ---
1 is starting
2 is starting
3 is starting
1 is ending
2 is ending
3 is ending
--- 主进程结束 ---
总耗时 :>>>> 3.282792568206787s
'''
【四】process对象的方法和属性
【1】查看当前进程的进程号
(1)什么是进程号
- 计算机会运行多个进程,而每一个运行的进程都会分配一个PID号
(2)如何查看进程号?
-
Windows系统 :cmd命令行 tasklist 即可查看
-
Mac系统 :终端运行 ps aux 即可查看
(3)如何根据指定进程号查看进程
-
Mac系统 :终端运行 ps aux|grep PID 即可查看
-
Windows系统:cmd 命令行 tasklist |findstr PID 即可查看
(4)查看进程号的方法1——current_process().pid
from multiprocessing import Process, current_process
import time
def task():
# 查看当前进程的 进程PID号
print(f'当前程序:{current_process().pid} 正在运行')
time.sleep(2)
if __name__ == '__main__':
p = Process(target=task)
p.start()
print(f'这是主程序:{current_process().pid}')
# 这是主程序:83272
# 当前程序:75652 正在运行
(5)查看进程号的方法2——os.getpid()
from multiprocessing import Process
import time, os
def task():
# 查看当前进程的 进程PID号
print(f'当前程序:{os.getpid()} 正在运行')
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
p.start()
print(f'这是主程序:{os.getpid()}')
# 这是主程序:81644
# 当前程序:72236 正在运行
(6)查看父进程的进程号的方法——os.getppid()
from multiprocessing import Process
import time, os
def task():
# 查看当前进程的进程PID号
print(f'当前程序:{os.getpid()} 正在运行')
# 查看当前进程的父进程PID号
print(f"当前程序的父进程:{os.getppid()} 正在运行")
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
p.start()
print(f'这是主程序:{os.getpid()}')
print(f"这是主程序的父进程:{os.getppid()}")
# 这是主程序:87128
# 这是主程序的父进程:90136
# 当前程序:91656 正在运行
# 当前程序的父进程:87128 正在运行
(5)杀死当前进程的方法——p.terminate()
- 告诉操作系统帮我去杀死当前进程,但是需要一定的时间,代码的运行速度极快
from multiprocessing import Process
import time, os
def task():
# 查看当前进程的进程PID号
print(f'当前程序:{os.getpid()} 正在运行')
# 查看当前进程的父进程PID号
print(f"当前程序的父进程:{os.getppid()} 正在运行")
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
p.start()
# 杀死当前进程
p.terminate()
print(f'这是主程序:{os.getpid()}')
print(f"这是主程序的父进程:{os.getppid()}")
# 这是主程序:92388
# 这是主程序的父进程:90136
(6)判断当前进程是否存活——p.is_alive()
from multiprocessing import Process
import time, os
def task():
# 查看当前进程的进程PID号
print(f'当前程序:{os.getpid()} 正在运行')
# 查看当前进程的父进程PID号
print(f"当前程序的父进程:{os.getppid()} 正在运行")
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
p.start()
# 杀死当前进程
p.terminate()
time.sleep(2)
# 判断当前进程是否存活
print(p.is_alive())
print(f'这是主程序:{os.getpid()}')
print(f"这是主程序的父进程:{os.getppid()}")
# False
# 这是主程序:93016
# 这是主程序的父进程:90136
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具