进程笔记(一)
第一节:进程
第二节: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 类实现多进程之间的数据传递
'''
'''