进程操作

进程操作

【一】multiprocessing模块介绍

  • python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。
    • Python提供了multiprocessing。
    • multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
  • multiprocessing模块的功能众多:
    • 支持子进程、通信和共享数据、执行不同形式的同步
    • 提供了Process、Queue、Pipe、Lock等组件。
  • 需要再次强调的一点是:
    • 与线程不同,进程没有任何共享状
    • 进程修改的数据,改动仅限于该进程内。

【二】Process类的介绍

【1】创建进程的类

  • 语法
Process([group [, target [, name [, args [, kwargs]]]]])
  • 由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

【2】参数介绍

  • group参数未使用,值始终为None
  • target表示调用对象,即子进程要执行的任务
  • args表示调用对象的位置参数元组,args=(1,2,'ly',)
  • kwargs表示调用对象的字典,kwargs=
  • name为子进程的名称

【3】方法介绍

  • p.start():启动进程,并调用该子进程中的p.run()

  • p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法

  • 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类的使用

  • 注意:在windows中Process()必须放到 if __name == 'main__':

(1)方法一:直接使用Process方法

import multiprocessing

def task(i):
    print(f'当前的进程为 :>>>> {i}')

def main_first():
    for i in range(1, 5):
        p = multiprocessing.Process(target=task, args=(i,))
        p.start()

if __name__ == '__main__':
    main_first()

# 当前的进程为 :>>>> 1
# 当前的进程为 :>>>> 2
# 当前的进程为 :>>>> 3
# 当前的进程为 :>>>> 4

(2)方法二:继承Process类

import multiprocessing

def task(i):
    print(f'当前的进程为 :>>>> {i}')

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

    def run(self) -> None:
        task(i=self.i)


def main_second():
    for i in range(1, 5):
        task = MyProcess(i=i)
        task.start()

if __name__ == '__main__':
    main_second()

# 当前的进程为 :>>>> 1
# 当前的进程为 :>>>> 2
# 当前的进程为 :>>>> 3
# 当前的进程为 :>>>> 4

(3)小结

  • 创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
  • 一个进程对应在内存中就是一块独立的空间
  • 多个进程对应在内存中就是多块独立的内存空间
  • 进程与进程之间数据默认情况下是无法直接交互的,如果想交互可以借助第三方工具或模块

【四】进程之间的内存空间是隔离的

  • 每一个子进程之间的数据是相互隔离的
  • 在执行子进程代码时,只修改自己子进程内的数据,不会影响到其他的子进程
import multiprocessing

a = 9

def task(i):
    global a
    a += 1
    print(f'当前的进程为 {i} :>>>> {a}')

def main_first():
    for i in range(1, 5):
        p = multiprocessing.Process(target=task, args=(i,))
        p.start()

if __name__ == '__main__':
    main_first()
# 当前的进程为 1 :>>>> 10
# 当前的进程为 2 :>>>> 10
# 当前的进程为 3 :>>>> 10
# 当前的进程为 4 :>>>> 10

【五】多进程实现TCP服务端并发

服务端

import socket
from socket import SOL_SOCKET, SO_REUSEADDR
import multiprocessing

server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8888))
server.listen()


def talk(conn, addr):
    while True:
        try:
            msg_client = conn.recv(1024)
            msg_client = msg_client.decode('utf8')
            print(f'这是来自 :>>> {addr} | 信息是 :>>>> {msg_client}')
            msg_client = msg_client.upper()
            conn.send(msg_client.encode('utf8'))
        except Exception as e:
            print(f'这是来自 :>>> {addr} | 错误是 :>>>> {e}')
            break


def main_multyprocess():
    while True:
        conn, addr = server.accept()
        task = multiprocessing.Process(target=talk, args=(conn, addr))
        task.start()


if __name__ == '__main__':
    main_multyprocess()

客户端

import socket

client = socket.socket()
client.connect(('127.0.0.1', 8888))
while True:
    msg = input("请输入小写字母:>>> ").strip()
    if not msg: continue

    client.send(msg.encode('utf8'))
    msg = client.recv(1024)
    print(f'这是来自服务端的数据 :>>>>{msg.decode("utf8")}')

【六】Process对象的join方法

  • 将并行转为串行
  • join:主进程等,等待子进程结束

串行

import multiprocessing,time


def run_task(i):

    print(f'{i}开始')
    time.sleep(2)
    print(f'{i}结束')


def main_first():
    a=[]
    for i in range(1, 5):
        p = multiprocessing.Process(target=run_task, args=(i,))
        p.start()
        p.join()


if __name__ == '__main__':
    start_time = time.time()
    main_first()
    print(f'这是程序运行的总耗时:{time.time() - start_time}')
    # 1
    # 开始
    # 1
    # 结束
    # 2
    # 开始
    # 2
    # 结束
    # 3
    # 开始
    # 3
    # 结束
    # 4
    # 开始
    # 4
    # 结束
    # 这是程序运行的总耗时: 8.235054731369019

并行

import multiprocessing,time


def run_task(i):

    print(f'{i}开始')
    time.sleep(2)
    print(f'{i}结束')


def main_first():
    a=[]
    for i in range(1, 5):
        p = multiprocessing.Process(target=run_task, args=(i,))
        p.start()
        a.append(p)
    for i in a:
        i.join()


if __name__ == '__main__':
    start_time = time.time()
    main_first()
    print(f'这是程序运行的总耗时:{time.time() - start_time}')
    
    # 1开始
    # 2开始
    # 3开始
    # 4开始
    # 4结束3结束
    # 
    # 2结束
    # 1结束
    # 这是程序运行的总耗时:2.0717782974243164

【六】Process对象的其他方法或属性

【1】什么是进程号

  • 一台计算机上面运行着很多进程,那么计算机是如何区分并管理这些进程服务端呢?
    • 计算机会给每一个运行的进程分配一个PID号

【2】如何查看进程号?

  • Windows系统
    • CMD 命令行 tasklist 即可查看
  • Mac系统
    • 终端运行 ps aux 即可查看

【3】如何根据指定进程号查看进程

  • Mac系统
    • 终端运行 ps aux|grep PID 即可查看
  • Windows系统
    • CMD 命令行 tasklist |findstr PID 即可查看

【4】方法

查看当前进程的进程号一

  • current_process().pid 方法
from multiprocessing import Process, current_process

def task():
    print(f'当前程序:>>>>{current_process().pid} 正在运行')


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
# 当前程序:>>>>1720 正在运行    

查看当前进程的进程号二

  • os.getpid() 方法
import multiprocessing,os


def task():
    print(f'当前程序:>>>>{os.getpid()} 正在运行')


if __name__ == '__main__':
    p = multiprocessing.Process(target=task)
    p.start()
    # 当前程序:>>>>16800 正在运行

查看当前进程的父进程的进程号

  • os.getppid() 方法
import multiprocessing,os


def task():
    print(f'当前程序:>>>>{os.getpid()} 正在运行')
    print(f'当前程序的父进程:>>>>{os.getppid()} 正在运行')

if __name__ == '__main__':
    p = multiprocessing.Process(target=task)
    p.start()
    # 当前程序: >> >> 3308 正在运行
    # 当前程序的父进程:>>>>20968 正在运行

杀死当前进程

  • p.terminate()

    • 告诉操作系统帮我去杀死当前进程

    • 但是需要一定的时间。

    • 代码的运行速度极快

import multiprocessing,os


def task(i):
    print(f'当前进程{i}:>>>>{os.getpid()} 正在运行')

def test():
    for i in range(1,5):
        p=multiprocessing.Process(target=task,args=(i,))
        p.start()
        if i==2:
            p.terminate()


if __name__ == '__main__':
    test()
    # 当前进程1:>>>>8872 正在运行
    # 当前进程3:>>>>1900 正在运行
    # 当前进程4:>>>>15180 正在运行

判断当前进程是否存活

  • p.is_alive()
import multiprocessing,os


def task(i):
    print(f'当前进程{i} :>>>> {os.getpid()} 正在运行')

def test():
    task_list={}
    for i in range(1,5):
        p=multiprocessing.Process(target=task,args=(i,))
        p.start()
        if i==2:
            p.terminate()
        task_list[i]=p
    for i,p in task_list.items():
        print(f'进程{i}是否存活 :>>> {p.is_alive()}')


if __name__ == '__main__':
    test()
    # 进程1是否存活 :>>> True
    # 进程2是否存活 :>>> False
    # 进程3是否存活 :>>> True
    # 进程4是否存活 :>>> True
    # 当前进程1 :>>>> 20796 正在运行
    # 当前进程3 :>>>> 304 正在运行
    # 当前进程4 :>>>> 20308 正在运行

【七】僵尸进程和孤儿进程

【1】僵尸进程

  • 对于子进程来说:子进程死亡的时候,但是他的这部分资源却没有被回收掉

  • 这种现象对于这个死掉的子进程来说就是僵尸进程

【2】孤儿进程

  • 子进程对于父进程来说:父进程死亡,子进程也应该跟这个死亡,父进程死了,但是子进程没死,
  • init 进程 接收掉所有父进程死亡而子进程未死亡的继承,负责回收掉子进程的资源

【3】僵尸进程的危害大于孤儿进程

  • 僵尸进程会占用大部分的资源,并且这部分资源没人来处理
  • 孤儿进程虽然父进程没了,但是 init 进程会将这部分资源释放掉

【八】守护进程

【1】什么是守护进程

  • 守护进程 (daemon) 是在计算机系统启动时就已经运行,并且一直在后台运行的一类特殊进程。
  • 它们通常不与用户直接交互,也不接受标准输入和输出,而是在后台执行某种任务或提供某种服务。
  • 守护进程往往是由系统管理员手动启动的,它们可以在系统启动时自动启动,一直运行在后台,直到系统关闭或被停止。
  • 常见的守护进程包括网络服务 (如 web 服务器、邮件服务器、 ftp 服务器等)、日志记录系统 (如系统日志服务、应用程序日志服务等) 等。
  • 守护进程通常在后台运行,不需要用户交互,并且有较高的权限,因此编写守护进程需要特别注意安全性和稳定性。

【2】主进程死亡,子进程未死亡

from multiprocessing import Process
import time


def task(name):
    print(f'总管:>>{name}>>正常存活')
    time.sleep(2)
    print(f'总管:>>{name}>>正常死亡')


if __name__ == '__main__':
    print(f'皇帝 :>>> qc >>> 执掌江山')
    p = Process(target=task, args=('aa',))

    p.start()

    print(f'皇帝 :>>> qc >>> 寿终正寝')
    # 皇帝 :>>> qc >>> 执掌江山
    # 皇帝 :>>> qc >>> 寿终正寝
    # 总管:>>aa>>正常存活
    # 总管:>>aa>>正常死亡 

【3】主进程死亡,子进程必死亡

from multiprocessing import Process
import time


def task(name):
    print(f'总管:>>{name}>>正常存活')
    time.sleep(2)
    print(f'总管:>>{name}>>正常死亡')


if __name__ == '__main__':
    print(f'皇帝 :>>> qc >>> 执掌江山')
    p = Process(target=task, args=('aa',))
    p.daemon=True
    p.start()
    print(f'皇帝 :>>> qc >>> 寿终正寝')
    # 皇帝 :>>> qc >>> 执掌江山
    # 皇帝 :>>> qc >>> 寿终正寝
posted @ 2024-04-22 16:44  蓝幻ﹺ  阅读(8)  评论(0编辑  收藏  举报