首页 » 编程 » Python » Python学习 » 网络编程进阶 » 正文

开启子进程的两种方式,查看进程的pid与ppid,其他方法

开启子进程的两种方式

方式一:

# 方式一:
from multiprocessing import Process
import time


def task(name):
    print("%s is running" % (name))
    time.sleep(3)
    print("%s is done" % (name))


if __name__ == '__main__':
    # p = Process(target=task, kwargs={"name": "子进程1"})  # kwargs表示调用对象的字典,kwargs={"name": "子进程1"}
    p = Process(target=task, args=("子进程1",))  # target表示调用对象,即子进程要执行的任务,参数args 元组形式,必须要有逗号
    p.start()  # 仅仅只是给操作系统发送了一个信号
    print("主")

运行输出:


子进程1 is running
子进程1 is done

 

方式二:

# 方式二
from multiprocessing import Process
import time


class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):   # 固定形式,名字必须是 run
        print("%s is running" % (self.name))
        time.sleep(3)
        print("%s is done" % (self.name))


if __name__ == '__main__':
    p = MyProcess("子进程1")
    p.start()  # 调用start 实际上就是调用run
    print("主")

运行输出:


子进程1 is running
子进程1 is done

 

查看进程的pid与ppid

通过 os.getpid()获取pid, 通过os.getppid()获取父进程id

from multiprocessing import Process
import time
import os


def task(name):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(3)
    print("%s is done, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))


if __name__ == '__main__':
    p = Process(target=task, args=("子进程1",))
    p.start()
    print("主进程,pid is:%s, 主进程的ppid is:%s" % (os.getpid(), os.getppid()))

运行输出:

主进程,pid is:9156, 主进程的ppid is:15936
子进程1 is running, pid is:15512, ppid is:9156
子进程1 is done, pid is:15512, ppid is:9156

 

join()

from multiprocessing import Process
import time
import os


def task(name):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(3)
    print("%s is done, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))


if __name__ == '__main__':
    p = Process(target=task, args=("子进程1",))
    p.start()
    p.join()  # 等待子进程执行完成之后再执行之后的程序
    print("主进程,pid is:%s, 主进程的ppid is:%s" % (os.getpid(), os.getppid()))

运行输出:

子进程1 is running, pid is:13620, ppid is:4336
子进程1 is done, pid is:13620, ppid is:4336
主进程,pid is:4336, 主进程的ppid is:15936

 

来进一步看join:

from multiprocessing import Process
import os


def task(name):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))


if __name__ == '__main__':
    p1 = Process(target=task, args=("子进程1",))
    p2 = Process(target=task, args=("子进程2",))
    p3 = Process(target=task, args=("子进程3",))
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print("主进程,pid is:%s, 主进程的ppid is:%s" % (os.getpid(), os.getppid()))

运行输出:

子进程2 is running, pid is:16784, ppid is:4584
子进程1 is running, pid is:10648, ppid is:4584
子进程3 is running, pid is:9376, ppid is:4584
主进程,pid is:4584, 主进程的ppid is:15936

 

解释:我们可以看到我启动了3个子进程,他们的运行顺序是不一样的,为什么这样呢,因为p1.start(), p2.start(), p3.start()虽然你看起来是同时启动的,但是实际上是发送信号给操作系统,而运行的顺序取决于操作系统,后面3个进程都加了join表示都要等待3个进程运行结束后再执行之后的程序,有人说 那这不就变成串行了吗,其实不是的,因为举个例子,加入p1运行需要2秒钟,p2运行需要5秒钟,p3运行需要3秒钟,如上面例子所示,程序实际运行时间为5秒,因为3个进程都同时向操作系统发送信号需要执行,然后3个进程都执行的join等待子进程运行结束,当还在等待p2执行完成的时候,p1和p3也在执行,等p2执行完,p1和p3早就已经执行完了,所以整个过程只需要5秒,看下面的例子把:

from multiprocessing import Process
import time
import os


def task(name, n):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(n)


if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=task, args=("子进程1", 2))
    p2 = Process(target=task, args=("子进程2", 5))
    p3 = Process(target=task, args=("子进程3", 3))
    # p1.start()
    # p2.start()
    # p3.start()
    # p1.join()
    # p2.join()
    # p3.join()

    # 简写为for循环
    p_list = [p1, p2, p3]
    for p in p_list:  # 写为for循环
        p.start()
    for p in p_list:  # 写为for循环
        p.join()
    print("主进程,pid is:%s, total runtime:%s" % (os.getpid(), time.time() - start))

 

运行输出:

子进程1 is running, pid is:10088, ppid is:5540
子进程2 is running, pid is:8504, ppid is:5540
子进程3 is running, pid is:19464, ppid is:5540
主进程,pid is:5540, total runtime:6.240571737289429

 

运行的最后时间为6.2秒。不是我们所说的5秒,但是相差不大,因为操作系调度原因,但是可以说明3个进程是并发运行的,而不是串行。

 

如果一定要实现串行也是可以的,换一种写法就可以:

from multiprocessing import Process
import time
import os


def task(name, n):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(n)


if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=task, args=("子进程1", 2))
    p2 = Process(target=task, args=("子进程2", 5))
    p3 = Process(target=task, args=("子进程3", 3))
    p1.start()
    p1.join()

    p2.start()
    p2.join()

    p3.start()
    p3.join()
    print("主进程,pid is:%s, total runtime:%s" % (os.getpid(), time.time() - start))

运行结果:

子进程1 is running, pid is:11856, ppid is:11708
子进程2 is running, pid is:19248, ppid is:11708
子进程3 is running, pid is:14572, ppid is:11708
主进程,pid is:11708, total runtime:14.602144479751587

总共耗时14秒。每一个子进程开启的时候,随即就在等待这个子进程的运行结束,然后再开启下一个子进程,所以每一个子进程都会等待他上一个子进程的结束,那么总的运行时间就累加起来了,运行的时候就是一个个运行的,也就是串行了。

 

p1.is_alive()   ,   p1.pid ,    p1.name

p1.is_alive()  查看 子进程的存活状态

p1.pid  查看子进程的pid,和os.getpid() 一样

p1.name   查看子进程的名字,由name参数决定,默认为 Process-1

来看下面的例子:


from multiprocessing import Process
import time
import os


def task(name, n):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(n)


if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=task, name="subprocess-1", args=("子进程1", 2))
    p1.start()
    print("进程是否存活:", p1.is_alive())
    p1.join()
    print("进程是否存活", p1.is_alive())
    print("进程的PID:", p1.pid)
    print("进程的名字:", p1.name)
    print("主进程,pid is:%s, total runtime:%s" % (os.getpid(), time.time() - start))

运行输出:

进程是否存活: True
子进程1 is running, pid is:6844, ppid is:15124
进程是否存活 False
进程的PID: 6844
进程的名字: subprocess-1
主进程,pid is:15124, total runtime:3.1747219562530518

 

p1.terminate()

p1.terminate()  发送终止信号给操作系统。

from multiprocessing import Process
import time
import os


def task(name, n):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(n)


if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=task, name="subprocess-1", args=("子进程1", 2))
    p1.start()
    p1.terminate()
    print("进程是否存活:", p1.is_alive())
    print("主进程,pid is:%s, total runtime:%s" % (os.getpid(), time.time() - start))

运行输出:

进程是否存活: True
主进程,pid is:17276, total runtime:0.021531343460083008

 

可以看到,为什么执行了p1.terminate()进程还是返回True存活的呢,因为p1.start() 和 p1.terminate()都是给操作系统发送信号,发送了启动信号后,随即就发送terminate信号,系统还来不及处理,所以马上查看进程是存活的,如果此时在terminate之后加一个sleep(2)或者join 等一下,则就会返回False了,如下:

from multiprocessing import Process
import time
import os


def task(name, n):
    print("%s is running, pid is:%s, ppid is:%s" % (name, os.getpid(), os.getppid()))
    time.sleep(n)


if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=task, name="subprocess-1", args=("子进程1", 2))
    p1.start()
    p1.terminate()
    time.sleep(2)

    print("进程是否存活:", p1.is_alive())
    print("主进程,pid is:%s, total runtime:%s" % (os.getpid(), time.time() - start))

运行输出:

进程是否存活: False
主进程,pid is:14264, total runtime:2.0259580612182617

 

练习题:

基于多进程实现并发的套接字通信?

服务端:

import socket
from multiprocessing import Process


def talk(conn):
    while True:
        data = conn.recv(1024)
        conn.send(data.upper())
    conn.close()


def server(ip, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((ip, port))
    server.listen(5)
    while True:
        print("等待连接...")
        conn, client_addr = server.accept()
        print("客户端已连接:", client_addr)
        p = Process(target=talk, args=(conn,))
        p.start()
        print("子进程已启动,pid为: %s,  进程名为:%s" % (p.pid, p.name))
    server.close()


if __name__ == '__main__':
    server("127.0.0.1", 9999)

 

客户端:

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 9999))
while True:
    msg = input(">>: ").strip()
    if not msg: continue
    client.send(msg.encode("utf-8"))
    data = client.recv(1024)
    print(data.decode("utf-8"))

 

连接2个客户端后服务端打印的信息如下:

等待连接…
客户端已连接: (‘127.0.0.1’, 22035)
子进程已启动,pid为: 6436, 进程名为:Process-1
等待连接…
客户端已连接: (‘127.0.0.1’, 22051)
子进程已启动,pid为: 11776, 进程名为:Process-2
等待连接…

 

每个客户端都能够正常执行。

 

发表评论

*