栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

【python基础】多任务编程之多进程

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【python基础】多任务编程之多进程

1、进程状态
三态:
    就绪态:进程具备执行条件,等待执行
    运行态:正占用cpu执行
    等待态:让出cpu,暂停执行
    
五态:
    新建
    终止
    
状态查看命令:
    ps -aux --->STAT列
        S:等待态
        R:执行态
        Z:僵尸

        <:有较高优先级
        N:优先级较低
        +:前台进程
        s:会话组组长
        l:有多线程的
2、父子进程
1、父子进程拥有独立的空间,各自使用自己的空间资源,子进程会复制父进程的全部内存空间,从fork下一句开始执行
2、父子进程各自独立运行,运行顺序不一定
3、父进程之前开辟的空间子进程同样拥有,父子进程对各自空间的操作不会相互影响
4、父子进程的退出不影响对方继续运行
3、孤儿和僵尸
孤儿进程:父进程先于子进程退出,此时子进程成为孤儿进程
孤儿进程会被系统进程收养,此时系统进程就会成为孤儿进程的新的父进程,孤儿进程退出该进程会自动处理(孤儿进程不会成为僵尸进程)

僵尸进程:子进程先于父进程退出,父进程又没有处理子进程的退出状态,此时子进程就会成为僵尸进程
僵尸进程虽然结束,但是会存留部分PCB在内存中,大量的僵尸进程会浪费系统内存资源

如何避免:
1、使用wait函数处理子进程退出:
	pid, status = os.wait()
	功能:阻塞函数,在父进程中阻塞等待子进程退出
	返回值:pid	退出的子进程pid;status 子进程退出状态
2、创建2级子进程处理僵尸:
	2.1、父进程创建子进程,等待回收子进程
	2.2、子进程创建二级子进程然后退出
	2.3、二级子进程成为孤儿,和父进程一起执行事件
3、通过信号处理子进程退出:
	子进程退出时会发送信号给父进程,如果父进程忽略子进程信号,则系统会自动处理子进程退出
	使用signal模块在父进程创建子进程前写如下语句:
		import signal
		signal.signal(signal.SIGCHLD, signal.SIG_IGN)
4、python实现多进程 4.1、os.fork

说明:只能在linux下调用,Windows内核中无fork函数

相关函数:

pid = os.fork()	
功能:创建新进程
返回值:int型
	在父进程中:返回子进程的pid;
    在子进程中:返回0
	创建失败:返回pid < 0;

	
pid = os.getpid()
返回值:当前进程的pid

pid = os.getppid()
返回值:父进程的pid

sys.exit([status])
功能:退出进程
参数:整数表示退出状态,字符串表示退出时打印的内容

pid, status = os.wait()
功能:阻塞函数,在父进程中阻塞等待子进程退出

示例:

import os
import sys

# -*- coding: utf-8 -*-
import os, sys
from time import sleep

def func1():
    for i in range(3):
        sleep(2)
        print("thing1")

def func2():
    for i in range(2):
        sleep(4)
        print("thing2")

pid = os.fork()     # 子进程从fork的下一句开始执行,子进程会copy父进程的全部内存空间
if pid < 0:         # pid < 0,创建子进程失败
    print("Create process failed")
elif pid == 0:      # 返回0,在子进程中,一级子进程
    p = os.fork()   
    if p == 0:      # 二级子进程
        func1()     # 在2级子进程中执行func1
    else:
        sys.exit(0) # 退出一级子进程,二级子进程成为孤儿进程
else:               # 父进程
    os.wait()       # 阻塞等待一级子进程退出,很快,因为创建完二级子进程一级子进程马上就退出了
    func2()         # 在父进程中执行func2

"""
思路:
    在一级子进程中创建2级子进程,然后马上退出1级子进程,二级子进程成为孤儿进程;
    在二级子进程中运行f1(),父进程中等待1级子进程退出(很快,因为创建完二级子进程马上就退出了),
    这样就实现了两个进程同时运行(分别在父进程和2级子进程中运行)而不产生僵尸进程
"""
4.2、multiprocessing
from multiprocessing import *
Process()
功能:创建进程对象
参数:target绑定要执行的目标函数
	args元组:用于给target函数位置传参
	kwargs字典:给target函数键值传参

p.start()
功能:启动进程

p.join([timeout])
功能:阻塞等待回收进程
参数:超时时间

multiprocessing总结:
1、使用multiprocessing创建进程同样是子进程复制父进程的空间代码段,父子进程运行互不影响
2、子进程只运行target绑定的函数部分,其余部分都是父进程执行内容
3、multiprocessing中父进程往往只用来创建子进程和回收子进程,具体事件由子进程完成
4、multiprocessing创建的子进程中无法使用标准输入

示例:

import time
from multiprocessing import *
# -*- coding:utf-8 -*-
"""
创建多个子进程执行任务,父进程只负责开始和回收,不做其他事
"""

from multiprocessing import Process
from time import sleep
import os

def add(a, b):
    sleep(3)
    print("a + b = %d" % (a + b))

def sub(a, b):
    sleep(3)
    print("a - b = %d" % (a - b))

if __name__ == "__main__":
    funcs = [add, sub]
    jobs = []
    start = time.time()
    for func in funcs:
        p = Process(target=func, args=(10,), kwargs={'b':3})
        jobs.append(p)          # 将进程对象保存,一起回收
        p.start()               # 注意:不能在start()后直接join(),因为join是阻塞函数。

    for i in jobs:              #一起回收
        i.join()
    print(time.time() - start)

运行结果如下:

a + b = 13
a - b = 7
3.151749849319458

每个程序都会sleep 3s,如果单进程执行,则总运行时间会超过6s。

5、进程池
进程池原理:
创建一定数量的进程来处理事件,事件处理完成进程不退出,而是继续处理其他事件,直到所有事件全都处理完毕统一销毁。
增加进程的重复利用,降低资源消耗

进程池实现:
1、创建进程池对象,放入适当进程
from multiprocessing import Pool
Pool(processes)
功能:创建进程池对象
参数:指定进程数量,默认根据系统自动判定

2、将事件加入进程池队列执行
pool.apply_async(func, args, kwds)
功能:使用进程池执行func事件
参数:func事件函数
    args:元组,给func按位置传参
    kwds:字典,给func按键值对传参

3、关闭进程池
pool.close()

4、回收进程池中的进程
pool.join()

示例:

# -*- coding:utf-8 -*-
"""
进程池示例
"""
import time
from multiprocessing import Pool
from time import sleep,ctime

def func01(data):
    sleep(5)
    print(data)

if __name__ == "__main__":

    start = time.time()
    #创建进程池
    pool = Pool(6)

    #向进程池添加事件
    for i in range(10):
        msg = "test %2d" % i
        pool.apply_async(func=func01, args=(msg,))

    #关闭进程池
    pool.close()

    #回收进程池
    pool.join()
    print(time.time() - start)

运行结果:

test  0
test  1
test  2
test  4
test  3
test  5
test  6
test  7
test  8
test  9
10.325045585632324
6、进程间通信 6.1、管道通信
管道通信:
Process--->Pipe--->Process
方法:
from multiprocessing import Pipe
fd1, fd2 = Pipe(duplex = True)
功能:创建管道
参数:默认双向管道
如果是False则是单向管道
返回值:表示管道两端的读写对象
    如果是双向管道,则均可读写
    如果是单向管道,则fd1只读,fd2只写

fd.recv()
功能:阻塞函数,从管道获取内容
返回值:获取到的数据

fd.send()
功能:向管道写入数据
参数:要写入的数据

注意:
1、multiprocessing中管道通信只能用于有亲缘关系的进程中(父子、兄弟)
2、管道对象在父进程中创建,子进程通过父进程获取

示例:

from multiprocessing import Pipe, Process

def func01(*args):
    args[0].send("func01 data to func02")
    data = args[0].recv()
    print("func01 received: " + data)

def func02(*args):
    data = args[0].recv()   #阻塞等待读取管道内容
    print("func02 received: " + data)
    args[0].send("func02 data to func01")

if __name__ == "__main__":
    pip1, pip2 = Pipe()
    p1 = Process(target=func01, args=(pip1,))
    p2 = Process(target=func02, args=(pip2,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

运行结果:

func02 received: func01 data to func02
func01 received: func02 data to func01
6.2、队列通信
队列通信:(读写队列,先进先出)
方法:
from multiprocessing import Queue
q = Queue(maxsize=0)
功能:创建队列对象
参数:最多存放消息个数
返回值:队列对象

q.put(data,[block,time])
功能:向队列存消息
参数:data:存入的内容
    block:设置是否阻塞,False为非阻塞,非阻塞时,对列满了就报错
    timeout:检测超时,只有在阻塞时才检测,超过超时时间就报错

q.get([block,time])
功能:从队列取出消息
参数:block:设置是否阻塞
    timeout:检测超时

q.full()    判断队列是否为满
q.empty()   判断队列是否为空
q.qsize()   获取队列中消息个数
q.close()   关闭队列

示例:

from multiprocessing import Queue,Process
from time import sleep
from random import sample,randint

def func01(q):
    l = [1, 2, 3, 4, 5]
    for i in l:
        q.put(i)

def func02(q):
    while True:
        try:
            print(q.get(timeout=2))
        except Exception as e:
            break

if __name__ == "__main__":
    q = Queue(5)
    p1 = Process(target=func01, args=(q,))
    p2 = Process(target=func02, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

运行结果:

1
2
3
4
5
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/968606.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号