多进程操作

【一】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
posted @   Ligo6  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示