3.terminate 方法

  • 作用:向操作系统申请强制终止进程。
  • 注意:使用 terminate 终止进程,不会执行 finally 代码块。
  • 辅助方法:is_alive() 可用于查看进程是否还“活着”。
import os
import time
from multiprocessing import Process

def speak():
    try:
        for index in range(10):
            print(f'我在说话{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
            time.sleep(1)
    # 注意:使用 terminate 终止进程,不会引起 finally 执行!
    finally:
        print('我是finally里的逻辑')

def study():
    for index in range(15):
        print(f'我在学习{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    p1 = Process(target=speak)
    p2 = Process(target=study)
    p1.start()
    p2.start()

    time.sleep(3)
    print('我是主进程,我准备强制终止p1进程........')
    # 向操作系统申请强制终止p1进程
    p1.terminate()
    # 等操作系统彻底终止掉了p1进程
    p1.join()
    # 看一下p1进程是否“活着”
    print(p1.is_alive())

    print('我是主进程中的【最后一行】打印')

4️⃣守护进程 (Daemon)

什么是守护进程?

一种 “依附于主进程存在的子进程”,一旦主进程结束,它就会被自动终止。简言之:主进程一死,守护进程必跟着死。

守护进程的使用场景:

  1. 后台监控类任务
  2. 日志 / 统计 / 采样 类任务
  3. 辅助型“陪跑任务”

注意点:

  1. 守护进程必须是 子进程。
  2. 主进程结束,守护进程也会随之结束。
  3. 守护进程中,不允许再创建新的子进程。
  4. 必须在 start 之前,start()之后,不能再设置daemon。
import os
import time
from multiprocessing import Process

def monitor():
    while True:
        try:
            with open('log.txt', 'r', encoding='utf-8') as file:
                lines = sum(1 for _ in file)
        except FileNotFoundError:
            lines = 0
        print(f'我是【守护进程({os.getpid()})】,log.txt 共有{lines}行')
        time.sleep(1)

def test():
    for index in range(30):
        print(f'我是test({os.getpid()})')
        time.sleep(1)

if __name__ == '__main__':
    print(f'我是主进程({os.getpid()})中的【第一行】代码')

    p1 = Process(target=monitor, daemon=True)
    p2 = Process(target=test)
    
    p1.start()
    p2.start()
    
    # 向文件中写入数据
    with open('log.txt', 'a', encoding='utf-8') as file:
        for index in range(10):
            file.write(f'大赛人{index}\n')
            file.flush()
            time.sleep(1)

    print(f'我是主进程({os.getpid()})中的【最后一行】代码')

6. 进程之间不共享变量

进程之间不共享内存,因此也就不共享任何变量。验证代码如下:

验证代码 1:

# 进程之间不共享内存,因此也就不共享任何变量。
from multiprocessing import Process
num = 100
names = []

def test1():
    global num, names
    num += 10
    names.append('张三')
    print(f'我是 test1 进程,操作之后的num是{num},操作之后的names是{names}')

def test2():
    global num, names
    num -= 10
    names.append('李四')
    print(f'我是 test2 进程,操作之后的num是{num},操作之后的names是{names}')

if __name__ == "__main__":
    print('主进程中的【第一行】代码')
    p1 = Process(target=test1)
    p2 = Process(target=test2)

    p1.start()
    p2.start()

    p1.join()
    p2.join()
    print('主进程中的【最后一行】代码', num, names)

验证代码 2:

# 进程之间不共享内存,因此也就不共享任何变量。
from multiprocessing import Process

def test1(num, names):
    num += 10
    names.append('张三')
    print(f'我是 test1 进程,操作之后的num是{num},操作之后的names是{names}')

def test2(num, names):
    num -= 10
    names.append('李四')
    print(f'我是 test2 进程,操作之后的num是{num},操作之后的names是{names}')

if __name__ == "__main__":
    num = 100
    names = []

    print('主进程中的【第一行】代码')
    p1 = Process(target=test1, args=(num, names))
    p2 = Process(target=test2, args=(num, names))

    p1.start()
    p2.start()

    p1.join()
    p2.join()
    print('主进程中的【最后一行】代码', num, names)

注意:

我们正常编写的变量,在多个进程之间是不共享的,但有些对象是天然被多个进程共享的,比如:我们之前讲过的 Lock 和 RLock,我们后面还会学习到很多天然被多个进程锁共享的对象,例如:multiprocessing.Queue

7. Queue(队列)

  • 我们之前学过 list、tuple、dict,它们的特点是:数据想怎么拿数据,就怎么拿。
  • 队列(Queue)是:一种“先进先出”的数据结构(先放进去的数据,一定会先取出来)。
  • 队列的常见操作如下:
import time
from multiprocessing import Queue, Process

# 创建一个队列(不限制大小,即:不设置容量上限)
q1 = Queue()

# 创建一个队列(最多能保存3个元素)
q2 = Queue(3)

# 1️⃣put方法:往队列里放数据(入队)
q1.put(10)
q1.put(20)
q1.put(30)

# 2️⃣get方法:从队列里取数据(出队)
value1 = q1.get()
value2 = q1.get()
value3 = q1.get()
print(value1)
print(value2)
print(value3)

# 3️⃣empty方法:判断队列是否为空
result = q1.empty()
print(result)

# 4️⃣full方法:判断队列是否已满
q1.put(10)
q1.put(20)
q1.put(30)
result = q1.full()
print(result)

q2.put(100)
q2.put(200)
q2.put(300)
result = q2.full()
print(result)

# 5️⃣qsize方法:获取队列长度
q1.put(10)
q1.put(20)
q1.put(30)
result = q1.qsize()
print(result)

# 6️⃣队列具备等待模式
q2.put(100)
q2.put(200)
q2.put(300)

# (1).当队列已满,继续 put,就会进入等待模式,等别人调用get方法,取走一个元素
q2.put(400)
print('放入完毕')

# (2).当队列已满,执行:put(元素, timeout=秒数),就会等待指定秒数。
q2.put(400, timeout=3)
print('放入完毕')

# (3).put_nowait 方法,会直接向队列中添加元素,不会进入等待模式,若队列已满,会抛出异常。
# 备注:put_nowait 等价于 put(元素, block=False),block的默认值为 True
q2.put_nowait(400)
q2.put(400, block=False)

# get读多了,也会进入等待模式
q2.get()
q2.get()
q2.get()

# (1).当队列已空,继续 get,就会进入等待模式。x
q2.get()

# (2).当队列已空,执行 get(timeout=秒数),就会等待指定秒数。
q2.get(timeout=3)

# (3).get_nowait 方法,会直接读取队列中的元素,不会进入等待模式,若队列已空,会抛出异常
# 备注:get_nowait 等价于 get(block=False)
q2.get_nowait()
q2.get(block=False)

通过多进程演示:当队列满了以后,再次put会等待,当有人从队列中取出元素后,put会继续。

def test(q):
    time.sleep(3)
    result = q.get()
    print('我从队列中取出了一个元素:',result)


# 通过多进程,演示一下:当队列满了以后,再次put会等待,当有人从队列中取出元素后,put会继续。
if __name__ == '__main__':
    # 创建一个队列,让其最多能保存2个元素
    q = Queue(2)
    # put两次,把队列填满
    q.put('大赛人')
    q.put('atguigu')
    print(f'队列是否已满:{q.full()}')

    # 创建子进程p1
    p1 = Process(target=test, args=(q, ))
    # 开启子进程p1,让其3秒钟后,从队列中取出一个元素
    p1.start()

    print('即将向已满的队列中添加一个元素........')
    q.put('hello')

    print('目前队列中有的元素是:')
    print(q.get())
    print(q.get())

8. 使用 Queue 实现进程通信

进程之间是不共享数据的,那如果进程之间如何进行数据沟通(进程通信)呢?手段多种多样,我们本小节给大家说通过Queue 实现进程通信。

核心思想: 一个进程负责生产数据,另一个进程负责消费数据,中间通过 Queue 进行“传话”

import time
from multiprocessing import Process, Queue

# 子进程1:往队列里放数据
def test1(q):
    for index in range(5):
        print(f'【test1】放入数据:{index}')
        q.put(index)
        time.sleep(0.5)

# 子进程2:从队列里取数据
def test2(q):
    for index in range(5):
        data = q.get()
        print(f'【test2】取出数据:{data}')
        time.sleep(1)

if __name__ == '__main__':
    q = Queue()

    p1 = Process(target=test1, args=(q,))
    p2 = Process(target=test2, args=(q,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

备注:q 是在主进程中创建的,但可以被子进程使用,因为 multiprocessing.Queue是跨进程的。

为什么数据不会乱掉? —— 因为队列是先进先出的。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容