一、创建进程的多种方式

1.创建进程的方式(需掌握的两种)

  • 双击桌面程序图标
  • 代码创建进程

2.multiprocess.process模块

2.1.process模块介绍

process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建

2.2.语法结构

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

参数介绍:

参数 描述
group 参数未使用,值始终为None
target 表示调用对象,即子进程要执行的任务
args 表示调用对象的未知参数元组,args = (1, 2, ‘jason’)
kwargs 表示调用对象的字典,kwargs =
name 子进程名称
  • 强调:
  1. 需要使用关键字的方式指定参数
  2. args指定的为传给target函数的位置参数,是一个元祖形式,必须有逗号

3.代码创建进程

代码示例:

from multiprocessing import Process
import time


def task(name):
    print(f'{name}正在运行')
    time.sleep(3)
    print(f'{name}运行结束')


if __name__ == '__main__':
    p = Process(target=task, args=('jason',))  # 创建一个进程对象
    p.start()  # 告诉操作系统创建一个进程(异步操作)
    # task('jason')  # 普通的函数调用是同步操作
    print('主进程')

创建进程的代码在不同的操作系统中底层原理的区别:

  • 在windows中:创建进程类似于导入模块

    if __name__ = '__main__':  # 启动脚本
    
  • 在mac/linux中:创建进程类似于拷贝,不需要启动脚本,但是为了兼容性,也可以使用

class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name
 
    def run(self):
        print(f'{self.name}正在运行')
        time.sleep(5)
        print(f'{self.name}运行结束')
 
 
if __name__ == '__main__':
    obj = MyProcess('jason')
    obj.start()
    print('主进程')

二、join方法

1.简介

join:主进程等待子进程运行结束后再运行

2.推导步骤

推导步骤1:直接在主进程代码中添加time.sleep()

  • 不合理,因为无法准确把握子进程执行的时间
from multiprocessing import Process
import time


def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{name}运行结束')


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason', 1))  # args就是通过元组的形式给函数传参
    p2 = Process(target=task, args=('kevin', 2))  # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    p3 = Process(target=task, args=('jerry', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()

推导步骤2:join方法

  • 非常合理
def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{name}运行结束')


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason', 1))  # args就是通过元组的形式给函数传参
    p2 = Process(target=task, args=('kevin', 2))  # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    p3 = Process(target=task, args=('jerry', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print('总耗时:%s' % end_time)
    print('主进程')

注意:一定要看准join的执行位置,以及多任务情况下等待的目标

三、进程间数据默认隔离

1.概念

多个进程数据彼此之间默认是相互隔离的,如果真的想交互需要借助管道或者队列

2.代码示例

from multiprocessing import Process

money = 100


def task():
    global money
    money = 666
    print('子进程打印的money', money)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print('父进程打印的money', money)

四、进程间通信(IPC机制)

IPC(Inter-Process Communication)

1.队列

1.1.概念

创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递(先进先出)

Queue([maxsize])
创建共享进程队列
maxsize:队列中允许的最大项数,忽略此参数则没有大小限制
底层队列使用管道和锁定实现

1.2.代码示例

from multiprocessing import Queue
# 1.创建队列对象
q = Queue(3)  # 括号内指定队列可以容纳的数据个数 默认:2147483647
# 2.往队列添加数据
# q.put(111)
# print(q.full())  # 判断队列是否已经存满
# q.put(222)
# q.put(333)
# print(q.full())
# q.put(444)  # 超出数据存放极限 那么程序一致处于阻塞态 直到队列中有数据被取出
# 3.从队列中取数据
print(q.get_nowait())
print(q.get_nowait())
print(q.get_nowait())  # 队列中如果没有数据可取 直接报错
# print(q.get())
# print(q.empty())  # 判断队列是否已经空了
# print(q.get())
# print(q.get())
# print(q.empty())
# print(q.get())  # 超出数据获取极限 那么程序一致处于阻塞态 直到队列中有数据被添加

注意:以下方法在多进程不能准确使用(失效)

q.full()
q.empty()
q.get_nowait()

2.IPC机制

  • 主进程与子进程通信
  • 子进程与子进程通信
from multiprocessing import Queue, Process


def procedure(q):
    q.put('子进程procedure往队里中添加了数据')


def consumer(q):
    print('子进程的consumer从队列中获取数据', q.get())


if __name__ == '__main__':
    q = Queue()  # 在主进程中产生q对象 确保所有的子进程使用的是相同的q
    p1 = Process(target=procedure, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    print('主进程')

五、生产者消费者模型

1.简介

生产者 消费者
产生数据 处理数据

2.例:爬取红牛分公司

生产者:获取网页数据的代码(函数)

消费者:从网页数据中筛选出符合条件的数据(函数)

3.完整的生产者消费者模型

完整的生产者消费者模型至少有三个部分:

  1. 生产者
  2. 消息队列/数据库
  3. 消费者

六、进程相关方法

1.查看进程号

from multiprocessing import current_process
import os


current_process().pid
os.getpid()
os.getppid()

2.销毁子进程

p1.terminate()

3.判断进程是否存活

p1.is_alive()

七、守护进程

1.概念

会随着主进程的结束而结束

主进程创建守护进程:

  • 其一:守护进程会在主进程代码执行结束后就终止
  • 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止(伴随守护对象存活而存活,死亡而死亡)

2.代码示例

from multiprocessing import Process
import time


def task(name):
    print('大内总管:%s存活' % name)
    time.sleep(3)
    print('大内总管:%s嗝屁' % name)


if __name__ == '__main__':
    p = Process(target=task, args=('基佬',))
    # p.daemon = True  # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束
    p.start()
    p.daemon = True  # 必须在start之前执行
    print('天子寿终正寝')

八、僵尸进程与孤儿进程

1.僵尸进程

进程已经运行结束,但是相关资源没有完全清空,需要父进程参与回收

2.孤儿进程

父进程意外死亡,子进程正常运行,该子进程就称为孤儿进程

  • 孤儿进程不是没有人管,操作系统会自动分配福利院接收

九、互斥锁

模拟抢票:查票、买票

from multiprocessing import Process
import time
import json
import random


# 查票
def search(name):
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))


# 买票
def buy(name):
    # 再次确认票
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1, 3))
    # 判断是否有票 有就买
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print('%s买票成功' % name)
    else:
        print('%s很倒霉 没有抢到票' % name)


def run(name):
    search(name)
    buy(name)


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=run, args=('用户%s'%i, ))
        p.start()
 posted on 2022-08-09 15:53  念白SAMA  阅读(26)  评论(0编辑  收藏  举报