代码改变世界

python之多进程

2018-07-09 22:37  鬼大斗  阅读(264)  评论(0编辑  收藏  举报

一.概述

1.什么是进程

进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。

2.进程与程序区别

程序仅仅只是一堆代码而已,而进程指的是程序的运行过程。

3.并发与并行

无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真是干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务。

并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发。

并行:同时运行,只有具备多个cpu才能实现并行。

4.进程的层次结构

无论UNIX还是windows,进程只有一个父进程,不同的是:

在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。

在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

5.进程的状态

在两种情况下会导致一个进程在逻辑上不能运行:

1.进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作。

2.与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。

因而一个进程由三种状态

二.进程的使用

1.multiprocessing模块介绍

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

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

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

2.process类介绍

创建进程的类:

Process([group [, target [, name [, args [, kwargs]]]]])

由该类实例化得到的对象,可用来开启一个子进程

强调: 1. 需要使用关键字的方式来指定参数

    2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍

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.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid

3.开启进程的两个方法

方法一

from multiprocessing import Process
import time


def task(name):
    print('%s is running' %name)
    time.sleep(3)
    print('%s is done' %name)


if __name__ == '__main__':
    # Process(target=task, kwargs={'name':'子进程1'})
    p = Process(target=task, args=('子进程1',))
    p.start()  # 仅仅只是发送一个信号

    print('')

方法二

from multiprocessing import Process
import time


class MyProcss(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print('%s is running' % self.name)
        time.sleep(3)
        print('%s is done' % self.name)


if __name__ == '__main__':
    p = MyProcss('子进程1')
    p.start()

    print('')

4.join方法和守护进程

a.join

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

情况一:在主进程的任务与子进程的任务彼此独立的情况下,主进程的任务先执行完毕后,主进程还需要等待子进程执行完毕,然后统一回收资源。

情况二:如果主进程的任务在执行到某一个阶段时,需要等待子进程执行完毕后才能继续执行,就需要有一种机制能够让主进程检测子进程是否运行完毕,在子进程执行完毕后才继续执行,否则一直在原地阻塞,这就是join方法的作用。

from multiprocessing import Process
import time,os


def task(name):
    print('%s is running,parent id is %s' % (os.getpid(), os.getppid()))
    time.sleep(3)
    print('%s is done,parent id is %s' % (os.getpid(), os.getppid()))


if __name__ == '__main__':
    p = Process(target=task, args=('子进程1',))
    p.start()  # 仅仅只是发送一个信号

    p.join()  # 等待子进程结束
    print('', os.getpid(), os.getppid())

b.守护进程

主进程创建子进程,然后将该进程设置成守护自己的进程,守护进程就好比崇祯皇帝身边的老太监,崇祯皇帝已死老太监就跟着殉葬了。

关于守护进程需要强调两点:

其一:守护进程会在主进程代码执行结束后就终止

其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

适用场景:

如果我们有两个任务需要并发执行,那么开一个主进程和一个子进程分别去执行就ok了,如果子进程的任务在主进程任务结束后就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程。主进程代码运行结束,守护进程随即终止。

from multiprocessing import Process
import time


def task(name):
    print('%s is running' % name)
    time.sleep(2)


if __name__ == '__main__':
    p = Process(target=task, args=('子进程1',))
    p.daemon = True  # 子进程还没启动就随主关闭
    p.start()
    print('')

5.互斥锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如下代码:

#并发运行,效率高,但竞争同一打印终端,带来了打印错乱
from multiprocessing import Process
import os,time
def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid())
if __name__ == '__main__': for i in range(3): p=Process(target=work) p.start()

加锁处理来控制输出

#由并发变成了串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process,Lock
import os,time
def work(lock): lock.acquire() #加锁 print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) lock.release() #释放锁

if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start()

6.队列

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。

from multiprocessing import Queue

q = Queue(3)

q.put('hello')
q.put({'a': 1})
q.put([3, 3, 3])
print(q.full())
# q.put(4) #再放就阻塞住了

print(q.get())
print(q.get())
print(q.get())
print(q.empty())  # 空了
# print(q.get()) #再取就阻塞住了