目录

一、multiprocessing模块介绍

二、进程

三、Process类

四、Process对象的join方法

五、守护进程


一、multiprocessing模块介绍

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu\_count\(\)查看),在python中大部分情况需要使用多进程。

Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,>提供了Process、Queue、Pipe、Lock等组件。

需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

二、进程

2.1 进程的理论

进程:进程是指一个程序在一个数据集上的动态执行过程(程序执行过程的抽象)

进程与程序的区别:程序仅仅是一堆代码而已,而进程指的是程序运行的过程。

进程运行的三种状态:

  • 运行:就绪态的进程被分配到CPU,进入运行状态;
  • 阻塞:进程执行过程中出现IO,进入阻塞状态(操作系统强行剥夺CPU),可以通过减少阻塞有效的来提高效率;
  • 就绪:等待CPU来执行的过程。进程占用CPU时间过长/出现一个优先级更高的进程,进入就绪状态(CPU被剥夺)。

2.2 开启进程的两种方式

方式一:调用内置的类

processing编程代码大全 编程 processing_Code

processing编程代码大全 编程 processing_Code_02

1 from multiprocessing import Process
 2 import time
 3 
 4 def task(name):
 5     print("%s is running" % name)
 6     time.sleep(3)
 7     print("%s is done" % name)
 8 
 9 if __name__ == "__main__":
10     # Process(target=task,kwargs={'name':'子进程1'})
11     p=Process(target=task,args=('子进程1',))  #args只有一个参数时,最后一定要加逗号,保证远足形式传递参数
12     p.start() #仅仅只是给操作系统发送了一个信号,等待cpu调度
13     print("主")

View Code

方式二:自定义类

processing编程代码大全 编程 processing_Code

processing编程代码大全 编程 processing_Code_02

1 from multiprocessing import Process
 2 import time
 3 
 4 
 5 class MyProcess(Process):
 6     def __init__(self,name):
 7         super().__init__()
 8         self.name=name
 9 
10     def run(self):  #必须是run这个名字
11         print('%s is running' % self.name)
12         time.sleep(3)
13         print('%s is done' % self.name)
14 
15 
16 if __name__ == '__main__':
17     p=MyProcess('子进程1')
18     p.start()  #本质上是在调用父类的start方法,而start方法下会触发run方法
19     print('主')

View Code

 

注意:在windows中Process()必须放到if __name == "__main__":下。由于在windows系统下,子进程是通过导入模块的方式拿到父进程的代码,如果没有main会一直开启子进程,而子进程的申请是需要开辟内存以及申请pid等。

三、Process类

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,可用来开启一个子进程。

强调:

  1. 需要使用关键字的方式来指定参数。如:target =×××,arges=(×××,)
  2. args制定的为传给taget函数的位置参数,是一个元组形式,必须有逗号。

参数介绍:

group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称

  

方法介绍:

p.start():       启动进程,并调用该子进程中的p.run()

p.run():          进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 

p.terminate():    强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁

p.is_alive():     如果p仍然运行,返回True

p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间。
                  需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

四、Process对象的join方法

在主进程运行过程中,如果想并发的执行其他任务,可以通过开启子进程实现,此时子进程的任务与主进程的任务分两种情况:

  1. 在主进程的任务与子进程的任务彼此独立的情况下,主进程的任务先执行完毕,主进程还需要等待子进程执行完毕,然后统一回收资源。
  2. 如果住进成的任务在执行到某一阶段时,需要等待子进程执行完毕后才能继续执行,就需要一种机制能够让主进程检测子进程是否执行完毕。主进程在子进程执行完毕后才继续执行,否则一直在原地阻塞。这就时join方法的作用。

举例如下:

processing编程代码大全 编程 processing_Code

processing编程代码大全 编程 processing_Code_02

1 from multiprocessing import Process
 2 import time,os
 3 
 4 def task(name,n):
 5     print('%s is running' %name)
 6     time.sleep(n)
 7 
 8 
 9 if __name__ == '__main__':
10     start=time.time()
11     p1=Process(target=task,args=('子进程1',5))
12     p2=Process(target=task,args=('子进程2',3))
13     p3=Process(target=task,args=('子进程3',2))
14     p_l=[p1,p2,p3]
15 
16     # p1.start()
17     # p2.start()
18     # p3.start()
19     for p in p_l:
20         p.start()
21 
22     # p1.join()
23     # p2.join()
24     # p3.join()
25     for p in p_l:
26         p.join()
27 
28     print('主',(time.time()-start))

join方法

说明:

p.join时让主进程等待p的结束,卡住的是主进程而不是子进程。

进程只要start就会在开始运行了,所以p1-p4.start()时,系统中已经有四个并发的进程了。

join是让主线程等,而p1-p3仍然是并发执行的,p1.join的时候,其余p2,p3仍然在运行,等p1.join结束,可能p2,p3早已经结束了,这样p2.join,p3.join直接通过检测,无需等待。所以3个join花费的总时间仍然是耗费时间最长的那个进程运行的时间

五、守护进程

主进程创建子进程,然后将该进程设置成守护自己的进程。关于守护进程需要强调两点:

  1. 守护进程在主进程代码执行结束就终止;
  2. 守护进程内无法在开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

processing编程代码大全 编程 processing_Code

processing编程代码大全 编程 processing_Code_02

1 from multiprocessing import Process
 2 import time
 3 import random
 4 
 5 
 6 def task(name):
 7     print('%s is running' % name)
 8     time.sleep(random.randrange(1,3))
 9     print("%s has gone" % name)
10 
11 
12 if __name__ == '__main__':
13     p = Process(target=task,args=('John',))
14     p.daemon = True #一定要在p.start()前设置,设置p为守护进程,机制p创建子进程,并且父进程代码之行结束,p即终止运行。
15     p.start()
16     print('主') #只要中断打印出这一行内容,那么守护进程P也就跟着结束了

View Code

  • p.daemon = True 一定要在p.start()前设置,设置p为守护进程,机制p创建子进程,并且父进程代码之行结束,p即终止运行。
  • 只要中断打印“print("主")”出这一行内容,那么守护进程P也就跟着结束了。

运行结果如下

processing编程代码大全 编程 processing_Code_09