进程

作者: 小吉头 | 来源:发表于2020-08-15 15:38 被阅读0次

概念

程序是静态的,程序运行起来后的代码+用到的资源(内存、摄像头、网络、硬盘等资源)叫进程,是操作系统分配资源的基本单元

进程的状态

实现多任务

import multiprocessing
import time

def test1():
    while True:
        print("----1----")
        time.sleep(1)

def test2():
    while True:
        print("----2----")
        time.sleep(1)

def main():
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()
    #print(p1.pid)  #打印子进程的pid
    #print(p2.pid)

if __name__ == '__main__':
    main()

>>>----1----
>>>----2----
>>>----1----
>>>----2----
...

也可以继承multiprocessing.Process

import multiprocessing
import time

class Process1(multiprocessing.Process):
    def run(self):
        while True:
            print("----1----")
            time.sleep(1)

class Process2(multiprocessing.Process):
    def run(self):
        while True:
            print("----2----")
            time.sleep(1)

def main():
    p1 = Process1()
    p2 = Process2()
    p1.start()
    p2.start()

if __name__ == '__main__':
    main()

运行后有三个进程,主进程,子进程1,子进程2。
正常情况,子进程不会修改代码,只会修改内存中的数据,所以只要把资源(内存、摄像头、网络、硬盘等资源)拷贝过来即可,和主进程共享一份代码,节省开销:


如果子进程通过特殊方法修改了代码,这时子进程复制主进程的代码+资源:


和线程的区别

进程是代码+资源的总称,一个进程中的多任务就叫线程。
多个线程使用的是同一份资源,多个进程有各自的资源。
拿着进程的资源去运行的是线程。上图中的红箭头其实都是线程在运行
进程是资源分配的单位,线程是操作系统调度的单位

不同的场景下应用

1、对于io操作,线程优于进程,因为线程切换开销比进程小。GIL遇到io操作会主动释放。
2、对于消耗cpu的操作,进程优于线程,进程可以充分发挥多核的优势,能并行执行。线程由于GIL的限制,同一时刻只有一个线程在一个cpu上执行,线程是并发执行。

进程池概念

创建固定数量的进程(有任务才创建,会有个最大值),进程池循环利用里面的进程去执行多任务,避免了频繁创建进程销毁进程的开销

进程池使用

方式一

from multiprocessing import Pool
import os,time

def run1(msg):
    print("%s开始执行,进程号是%d"%(msg,os.getpid()))
    time.sleep(1)

if __name__ == "__main__":
    po = Pool(2)#最多两个进程,现在不会创建,使用到的时候创建
    for i in range(0, 6):#添加了6个任务,虽然超过了进程池大小,不会报错,会先存起来。空闲的子进程会从任务队列中取任务执行。
        #添加任务到任务队列
        result = po.apply_async(run1, (i,)) #apply_async()返回的是ApplyResult对象,可以通过get()获取返回值(该方法会阻塞主进程一直等待子进程的返回值),还有其他方法可以查看ApplyResult类
    print('start')
    po.close()#关闭进程池,关闭后po不会再接收新的请求
    po.join()#等待po中所有子进程执行完成,必须放在close语句后面
    print('end')
>>>start
>>>0开始执行,进程号是7404
>>>1开始执行,进程号是8040
>>>2开始执行,进程号是7404
>>>3开始执行,进程号是8040
>>>4开始执行,进程号是7404
>>>5开始执行,进程号是8040
>>>end

通过进程池创建的任务主进程不会等待子进程执行完成,如果没有调用join(),主进程执行完会直接结束,子进程的任务也跟着结束了。
通过multiprocessing.Process创建的子进程,主进程会等待子进程执行完成再退出。

方式二

from concurrent.futures import ProcessPoolExecutor,as_completed
import os,time

def run1(msg):
    print("%s开始执行,进程号是%d"%(msg,os.getpid()))
    time.sleep(1)

if __name__ == "__main__":

    with ProcessPoolExecutor(2) as executor:
        all_task = [executor.submit(run1,i) for i in range(0,6)]
        for future in as_completed(all_task):
            pass

    print('end')

>>>0开始执行,进程号是6844
>>>1开始执行,进程号是804
>>>2开始执行,进程号是6844
>>>3开始执行,进程号是804
>>>4开始执行,进程号是6844
>>>5开始执行,进程号是804
>>>end

进程间通信

1、使用队列:

from multiprocessing import Queue
q = Queue(3) #初始化Queue对象,最多可接收三条put消息
q.put(1)
q.put('abc')
print(q.full())
q.put(['a','b','c'])
print(q.full())


try:
    q.put_nowait('test') #q.put('test'),如果队列已满,会阻塞,不会像put_nowait()一样抛异常
except:
    print('消息队列已满,现有消息数量:%s'%q.qsize())


if not q.empty():
    for i in range(q.qsize()):
        print(q.get_nowait()) #q.get()如果队列为空,会阻塞,不会像get_nowait()一样抛异常

多进程下不支持使用from queue import Queue,会抛异常:TypeError: can't pickle _thread.lock objects
multiprocessing .Queue作为中间介质:


import multiprocessing

def download_data(q):
    data = [1,2,3,4]
    #向队列中写入数据
    for temp in data:
        q.put(temp)


def deal_data(q):
    #从队列中获取数据
    resList = list()
    while True:
        data = q.get()
        resList.append(data)
        if q.empty():
            break
    print(resList)

def main():
    #1、创建一个队列
    q = multiprocessing.Queue()
    #2、创建多个进程,将队列的引用传递过去
    p1 = multiprocessing.Process(target=download_data,args=(q,))
    p2 = multiprocessing.Process(target=deal_data,args=(q,))
    p1.start()
    p2.start()

if __name__ == "__main__":
    main()
>>>[1,2,3,4]

如果在主进程中创建列表,通过共享变量的方式传递给子进程可以实现数据共享吗?

def addData1(tmp_list):
    tmp_list.append(1)
    print('进程1',tmp_list)

def addData2(tmp_list):
    tmp_list.append(2)
    print('进程2',tmp_list)

def main():
    tmp_list = []
    p1 = multiprocessing.Process(target=addData1,args=(tmp_list,))
    p2 = multiprocessing.Process(target=addData2,args=(tmp_list,))
    p1.start()
    p2.start()
    time.sleep(2)
    print('主进程',tmp_list)

if __name__ == "__main__":
    main()

>>>进程1 [1]
>>>进程2 [2]
>>>主进程 []

前面说过一般情况下,多进程共用一份代码,子进程拷贝主进程资源,各自独立
子进程中tmp_list = [],相当于各自拿同一份代码在执行,互不影响

multiprocessing .Queue在进程池中失效

下面的代码在进程池中使用了多进程的队列,没有输出结果

import multiprocessing

def download_data(q):
    data = [1,2,3,4]
    #向队列中写入数据
    for temp in data:
        q.put(temp)


def deal_data(q):
    #从队列中获取数据
    resList = list()
    while True:
        data = q.get()
        resList.append(data)
        if q.empty():
            break
    print(resList)

def main():
    q = multiprocessing.Queue()
    po = multiprocessing.Pool(2)
    po.apply_async(download_data, (q,))
    po.apply_async(deal_data, (q,))
    po.close()
    po.join()

if __name__ == "__main__":
    main()

q = multiprocessing.Queue()修改成q = multiprocessing.Manager().Queue()后可以正常输出

2、使用管道

pipe适用于只有两个进程通信的场景,性能高于queue

from multiprocessing import Process,Pipe

def download_data(pipe):
    pipe.send([1,2,3])

def deal_data(q):
    print(q.recv())

def main():
    recv_pipe,send_pipe = Pipe()
    p1 = Process(target=download_data,args=(send_pipe,))
    p2 = Process(target=deal_data,args=(recv_pipe,))

    p1.start()
    p2.start()

if __name__ == "__main__":
    main()

>>>[1,2,3]

3、Manager()提供的共享变量

将上面的tmp_list修改成multiprocessing.Manager().list(),可以定义一个进程间共享的list()变量。Manager()还提供了其他的数据类型。

import multiprocessing
import time

def addData1(tmp_list):
    tmp_list.append(1)
    print('进程1',tmp_list)

def addData2(tmp_list):
    tmp_list.append(2)
    print('进程2',tmp_list)

def main():
    tmp_list = multiprocessing.Manager().list()
    p1 = multiprocessing.Process(target=addData1,args=(tmp_list,))
    p2 = multiprocessing.Process(target=addData2,args=(tmp_list,))
    p1.start()
    p2.start()
    time.sleep(2)
    print('主进程',tmp_list)

if __name__ == "__main__":
    main()

>>>进程2 [2]
>>>进程1 [2, 1]
>>>主进程 [2, 1]

相关文章

  • 进程,进程,进程

    1. 进程是具有独立功能的程序关于某个数据集合的一次运行过程。(1)程序本身是静态的,是没有生命周期的,只有运行起...

  • Linux回收子进程

    孤儿进程 孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程...

  • Android 五种进程的管理

    安卓有几种进程? 前台进程 可见进程 服务进程 后台进程 空进程 前台进程 用户当前操作所必需的进程。如果一个进程...

  • 孤儿进程、僵尸进程与进程回收

    孤儿进程与僵尸进程 孤儿进程:父亲死了,子进程被init进程领养僵尸进程:子进程死了,父进程没有回收子进程的资源(...

  • 第三章 进程管理

    进程基础 进程基本概念 进程组:只包括祖先进程,子孙进程,兄弟进程进程树:所有进程僵尸进程:在父进程中经常会调用w...

  • Chaprter-1 [进程]

    进程模型 进程定义 进程的创建 进程的终止 进程的层次结构 进程的状态 进程的状态图 进程的实现

  • 进程操作

    一、进程创建 进程树 父进程创建若干子进程,子进程再创建其子进程,依次类推,构成进程树。进程的调度,其实就是在进程...

  • 进程管理(一)进程操作

    进程创建 、进程执行映像和加载、进程运行(进程调度)、进程间的互斥与同步、进程间通信、进程终止 1、进程创建 PC...

  • python中的僵尸进程和孤儿进程

    孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被 init 进程(进程号为 1)所收养,...

  • 容器中的孤儿进程&僵尸进程简介

    背景简介 孤儿进程 父进程先于子进程退出,那么子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)接管,并...

网友评论

      本文标题:进程

      本文链接:https://www.haomeiwen.com/subject/ecgidktx.html