进程笔记(一)

第一节:进程

第二节:Python多进程示例

第三节:Process参数说明

第四节:获取进程编号

第五节:多进程带参数的任务

第六节:进程之间不共享全局变量

第七节:主进程会等待所有的子进程执行结束再结束

第八节:Process子类创建子进程

第九节:进程池

第十节:进程间通信

1、进程

一个正在运行的程序或者软件就是一个进程

  • 操作系统进行资源分配的基本单位
  • 每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行
  • 一个程序运行后至少有一个进程
  • 一个进程默认有一个线程
  • 进程里面可以创建多个线程
  • 线程是依附在进程里面的,没有进程就没有线程
  • 真正干活的是线程

主进程就像是一个集团,集团下面有很多子公司(子进程),集团本身也是一个公司,每个公司真正干活的是不同岗位的员工(线程/多线程)

2、Python多进程示例

multi_process.py

import multiprocessing
import time

def listen_music():
    for i in range(3):
        print('听音乐......','进程名:%s'%multiprocessing.current_process().name)
        time.sleep(0.2)

def read_book():
    for i in range(3):
        print('看书......','进程名:%s'%multiprocessing.current_process().name)
        time.sleep(0.2)

if __name__ == '__main__':
    # 创建听音乐进程(子进程)
    listen_music_process = multiprocessing.Process(target=listen_music,name='我是听音乐的进程')
    # 创建看书进程 (子进程)
    read_book_process = multiprocessing.Process(target=read_book,name='我是看书的进程')
    # 启动子进程
    listen_music_process.start()
    read_book_process.start()

运行结果

听音乐...... 进程名:我是听音乐的进程
看书...... 进程名:我是看书的进程
听音乐...... 进程名:我是听音乐的进程
看书...... 进程名:我是看书的进程
听音乐...... 进程名:我是听音乐的进程
看书...... 进程名:我是看书的进程

3、Process参数和方法说明

Process 继承于 BaseProcess

class BaseProcess(object):

    def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
                 *, daemon=None):

参数说明:

  • group:指定进程组,默认为None (官方解释:group 应该始终是 None ;它仅用于兼容 threading.Thread 。 target 是由 run() 方法调用的可调用对象)
  • target:执行的目标任务名(传入函数名字,不是函数调用)
  • name:进程名字 (可以使用该参数指定进程名,multiprocessing.current_process().name 可以获取进程名)
  • args:以元组方式给执行任务传参 (给target指定的函数/方法传参)
  • kwargs:以字典方式给执行任务传参(给target指定的函数/方法传参)

常⽤⽅法:

  • is_alive():判断进程实例是否还在执⾏;
  • join(self, timeout=None):等待子进程终止 ;
  • start():启动进程实例(创建⼦进程);
  • run():如果没有指定target的任务,对这个对象调⽤start()⽅法时,就将执
    ⾏对象中的run()⽅法;
  • terminate():⽴即终⽌进程;

4、获取进程编号

import os
os.getpid()   表示获取当前进程编号
os.getppid()  表示获取当前父进程编号

获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的

multi_process_id.py

import multiprocessing
import time
import os

def listen_music():

    # 获取当前进程的编号
    print('listen_music 进程当前编号:',os.getpid())
    # 获取当父进程的编号
    print('listen_music 父程当前编号:',os.getppid())
    for i in range(3):
        print('听音乐......','进程名:%s'%multiprocessing.current_process().name)
        time.sleep(0.2)

def read_book():

    # 获取当前进程的编号
    print('read_book 进程当前编号:', os.getpid())
    # 获取当父进程的编号
    print('read_book 父进程当前编号:', os.getppid())
    for i in range(3):
        print('看书......','进程名:%s'%multiprocessing.current_process().name)
        time.sleep(0.2)

if __name__ == '__main__':
    # 获取当前进程的编号
    print("主进程 main 编号:", os.getpid())

    # 创建听音乐进程(子进程)
    listen_music_process = multiprocessing.Process(target=listen_music,name='我是听音乐的进程')
    # 创建看书进程 (子进程)
    read_book_process = multiprocessing.Process(target=read_book,name='我是看书的进程')
    # 启动子进程
    listen_music_process.start()
    read_book_process.start()

运行结果

主进程 main 编号: 2281
listen_music 进程当前编号: 2283
listen_music 父程当前编号: 2281
听音乐...... 进程名:我是听音乐的进程
read_book 进程当前编号: 2284
read_book 父进程当前编号: 2281
看书...... 进程名:我是看书的进程
听音乐...... 进程名:我是听音乐的进程
看书...... 进程名:我是看书的进程
听音乐...... 进程名:我是听音乐的进程
看书...... 进程名:我是看书的进程

5、多进程带参数的任务

  • args 表示以元组的方式给执行任务传参
  • kwargs 表示以字典方式给执行任务传参

multi_process_args.py

import multiprocess
import time

def task(task_name):
    for i in range(3):
        print('我的任务名:',task_name)
        time.sleep(0.2)


if __name__ == '__main__':
    # 任务一:听歌
    task1 = multiprocessing.Process(target=task,args=('听歌...',))
    # 任务二:看书
    task2 = multiprocessing.Process(target=task,kwargs={'task_name':'看书...'})

    # 启动任务
    task1.start()
    task2.start()

运行结果

我的任务名: 听歌...
我的任务名: 看书...
我的任务名: 听歌...
我的任务名: 看书...
我的任务名: 看书...
我的任务名: 听歌..

6、进程之间不共享全局变量

创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

multi_process_global_var.py

import multiprocessing
import os

MY_LIST = []


def task(task_name):
    print(task_name, '的父进程id:', os.getppid())
    for i in range(3):
        print(i, task_name, '的进程id', os.getpid())
        MY_LIST.append(i)
    print(task_name, ',MY_LIST:', MY_LIST)


if __name__ == '__main__':
    print('主进程id:', os.getppid())
    task1 = multiprocessing.Process(target=task, args=('task1',))
    task2 = multiprocessing.Process(target=task, args=('task2',))
    task1.start()
    task2.start()
    task1.join()
    task2.join()
    print('主进程 MY_LIST', MY_LIST)

运行结果

主进程id: 14628
task2 的父进程id: 1976
0 task2 的进程id 17040
1 task2 的进程id 17040
2 task2 的进程id 17040
task2 ,MY_LIST: [0, 1, 2]
task1 的父进程id: 1976
0 task1 的进程id 16436
1 task1 的进程id 16436
2 task1 的进程id 16436
task1 ,MY_LIST: [0, 1, 2]
主进程 MY_LIST []

7、主进程会等待所有的子进程执行结束再结束

孤儿进程

正常情况下,主进程会等待所有的子进程执行结束再结束,但是如果主进程突然被Kill,剩下的进程就叫做"孤儿进程"

僵尸进程

如果子进程在exit()之后,父进程没有来得及处理,那么保留的那段信息就不会释放,其进程号就会一直被占用, 这种进程称为“僵尸进程”

8、Process子类创建子进程

Process类 run 方法

如果没有给定target参数,对这个对象调⽤start()⽅法时,就将执
⾏对象中的run()⽅法

multi_process_run

import os
from multiprocessing import Process


class MyMultiProcess(Process):

    def __init__(self, task_name):
        Process.__init__(self)
        self.task_name = task_name

    def run(self):
        for i in range(3):
            print(i, self.task_name, '的进程id', os.getpid(),'---','父进程id:',os.getppid())


if __name__ == '__main__':
    print('父进程id:', os.getpid())
    p1 = MyMultiProcess('task1')
    p2 = MyMultiProcess('task2')
    p1.start()
    p2.start()

运行结果

父进程id: 15292
0 task1 的进程id 6596 --- 父进程id: 15292
0 task2 的进程id 12540 --- 父进程id: 15292
1 task1 的进程id 6596 --- 父进程id: 15292
1 task2 的进程id 12540 --- 父进程id: 15292
2 task1 的进程id 6596 --- 父进程id: 15292
2 task2 的进程id 12540 --- 父进程id: 15292

9、进程池

进程池用于快速大量生成进程任务,把所有进程任务添加到进程池,进程池有一个同时执行的最大进程数,当任务数量多余设置的最大进程数,那么后面的任务会进入等待,任务先进先出。

multi_process_pool

from multiprocessing import Pool
import os

def task(task_name):
    for i in range(3):
        print(task_name, '的进程id', os.getpid(), '---', '父进程id:', os.getppid())

if __name__ == '__main__':
    # 定义⼀个进程池,最⼤进程数2
    process_pool = Pool(2)
    # 往进程添加任务
    for i in range(3):
        # 非堵塞的方式添加任务
        process_pool.apply_async(func=task,args=('任务%s'%(i+1),))

    # 关闭进程池 , 关闭后不再添加新的任务
    process_pool.close()
    process_pool.join()

运行结果

任务1 的进程id 8624 --- 父进程id: 6372
任务2 的进程id 17188 --- 父进程id: 6372
任务1 的进程id 8624 --- 父进程id: 6372
任务2 的进程id 17188 --- 父进程id: 6372
任务1 的进程id 8624 --- 父进程id: 6372
任务2 的进程id 17188 --- 父进程id: 6372
任务3 的进程id 8624 --- 父进程id: 6372
任务3 的进程id 8624 --- 父进程id: 6372
任务3 的进程id 8624 --- 父进程id: 6372  

Pool常⽤方法:

  • apply_async(func, args=(), kwds={}) :使⽤⾮阻塞⽅式调⽤func(并⾏执
    ⾏,堵塞⽅式必须等待上⼀个进程退出才能执⾏下⼀个进程),args为
    传递给func的参数列表,kwds为传递给func的关键字参数列表;
  • apply(func, args=(), kwds={}):使⽤阻塞⽅式调⽤func
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,⽴即终⽌;
  • join():主进程阻塞,等待⼦进程的退出, 必须在close或terminate之后
    使⽤;

10、进程间通信

multiprocessing 模块的 Queue 类实现多进程之间的数据传递

'''

'''

原文地址:https://www.cnblogs.com/snailrunning/p/12169209.html