1.多进程
由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分需要使用进程.Python提供了非常好的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情.借助这个包,可以轻松完成从单进程到并发执行的转换.multiprocessing支持子进程,通信和共享数据,执行不同形式的同步,提供process,queue,pipe,lock等组件。
  multiprocessing包是Python中的多进程管理包.与threading.Thread类似,它可以利用multiprocessin.Process对象来创建一个进程.该进程可以运行Python程序内部编写的函数.该Process对象与Thread对象的用法相同,也有start(),run(),join的方法,此外multiprocessing包中也有lock/Event/Semophore/Condition类(这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致.所以,multiprocessing的很大一部分与threading使用同一套API,只不过换到多进程的情境。
在使用这此共享API的时候,我们要注意以下几点:
- 在UNIX平台上,当某个进程终结之后,该进程需要被父进程调用wait,否则进程成为僵尸进程。所以,有必要对每个Process对象调用join()方法(实际上等同于wait).对于多线程来说,由于只有一个进程,所以不存在此必要性
- multiprocessing提供了threading包中没有的IPC(比如Pipe和Queue),效率上更高。应优先考虑Pipe和Queue,避免使用lock/event/semaphore/condition等同步(因为它们占据的不是用户进程的资源)
- 多进程应该避免共享资源。在多线程中,比较容易使用共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以以方法并不合适。此时可以共享内存和mangaer的方法共享资源。但这样做提高了程序的复杂度,并因为同步需要而降低了程序的效率。
ProcessPID中保存有PID,如果进程还没有start(),即PID为None
在windows系统下,需要注意的是要想启动一个子进程,必须加上那句if__name__=="main",进程相关的要写在这句下面.

 

2.实例分析

[root@node2 multiprocess]# cat multi-process.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process    #引用进程模块
import time
def f(name):
    time.sleep(1)
    print('hello',name,time.ctime())

if __name__=='__main__':
    p_list=[]
    for i in range(3):
        p = Process(target=f, args=('reid',))  #创建一个进程对象,再传入一个函数f为参数
        p_list.append(p)
        p.start()
    for p in p_list:
        p.join()
    print('end')
[root@node2 multiprocess]# python3 multi-process.py  #程序中有一个主进程,三个子进程,进程之间是独立运行的,互不干扰
hello reid Sun Jun 10 10:13:57 2018
hello reid Sun Jun 10 10:13:57 2018
hello reid Sun Jun 10 10:13:57 2018
end

类式调用

[root@node2 multiprocess]# cat muti-process-class.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process
import time

class MyProcess(Process):  #继承multiprocessing下的Process
    def __init__(self):
        super(MyProcess,self).__init__()  #继承父类的init方法

    def run(self):
        time.sleep(1)
        print('hello',self.name,time.ctime())   
        #三个进程同时打印,self.name本身有值,进程对象下的一个属性,进程名,如进程1,进程2等,也可以赋值,在实例化对象时,传入一个参数

if __name__=='__main__':
    p_list=[]
    for i in range(3):
        p = MyProcess()      #进程对象
        p.start()            #启动
        p_list.append(p)

    for p in p_list:
        p.join

    print('end')
[root@node2 multiprocess]# python3 muti-process-class.py
end
hello MyProcess-1 Sun Jun 10 11:43:24 2018
hello MyProcess-3 Sun Jun 10 11:43:24 2018
hello MyProcess-2 Sun Jun 10 11:43:24 2018

父进程与子进程间的关系

[root@node2 multiprocess]# cat par-chil.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process
import os
import time
def info(title):
    print(title)             #传输什么,打印什么
    print('module name: ',__name__)          #__name__是main
    print('parent prcess: ',os.getppid())     #打印父进程号
    print('process id: ', os.getpid())        #打印进程号

def f(name):
    info('\033[31;1mfunction f\033[0m')
    print('hello',name)

if __name__=='__main__':
    info('\033[32;1mmain process line \033[0m')  #传入参数,执行info函数
    time.sleep(3)
    p = Process(target=info,args=('bob',))  #创建子进程
    p.start()
    p.join()
[root@node2 multiprocess]# python3 par-chil.py
main process line 
module name:  __main__   主进程
parent prcess:  13838
process id:  14521       ###
bob
module name:  __main__   子进程
parent prcess:  14521    ###
process id:  14522

  

3.进程通信和数据共享
分析:线程可以共享数据,进程之间实现通信的方法是使用pipe和queue
(1)、Queue

[root@node2 multiprocess]# cat que.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process,Queue
def f(q):
    q.put([42,2,'hello'])

if __name__=='__main__':
    q = Queue()         #进程队列
    p_list=[]
    for i in range(3):
        p = Process(target=f,args=(q,)) #q作为参数由父进程传给子进程,因为正确情况下,父进程和子进程不能通信
        p_list.append(p)
        p.start()
    print(q.get())
    print(q.get())
    print(q.get())
    for i in p_list:
            i.join()
[root@node2 multiprocess]# python3 que.py
[42, 1, 'hello']
[42, 0, 'hello']
[42, 2, 'hello']

确认def f中的q和p = Process(target=f,args=(q,i))中的q

[root@node2 multiprocess]# cat que.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process,Queue
def f(q):
    q.put([42,2,'hello'])
    print('subprocess q id: ', id(q))   ###id一样表示是共享的,不一样表示copy的

if __name__=='__main__':
    q = Queue()
    p_list=[]
    print('main q id: ', id(q))  ###

    for i in range(3):
        p = Process(target=f,args=(q,))
        p_list.append(p)
        p.start()
    print(q.get())
    print(q.get())
    print(q.get())
    for i in p_list:
            i.join()
[root@node2 multiprocess]# python3 que.py
main q id:  140394463540392
subprocess q id:  140394463540392   #windows测试可能不一样
[42, 2, 'hello']
subprocess q id:  140394463540392
subprocess q id:  140394463540392
[42, 2, 'hello']
[42, 2, 'hello']

  

(2)、Pipe
分析:父进程处理阻塞状态等待接收,子进程进行发送数据

[root@node2 multiprocess]# cat pipes.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process,Pipe
def f(conn):
    conn.send([42,None,'hello'])
    conn.close()

if __name__=='__main__':
    parent_conn,child_conn = Pipe()  #父进程和字进程的通道
    p = Process(target=f,args=(child_conn,))   
    #把子进程的通道作为参数传入子进程,通过函数f可以进行send和reveice,而且在主进程也有parent conn                        
    p.start()                   #启动一个子进程
    print(parent_conn.recv())   #父进程在等待接收(阻塞),直到子进程执行f函数时,有个子进程的管道进行conn.send发送数据,parent_conn可以接收
    p.join()
[root@node2 multiprocess]# python3 pipes.py
[42, None, 'hello']

  

(3)、Manager

描述: 数据共享
[root@node2 multiprocess]# cat manager.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process, Manager
def f(d,l,n):     #d是字典,l是列表,n是i一个0-9的值
    d[n] = '1'    #d实际是manager创建的
    d['2'] = 2
    d[0.25] = None
#字典d,当一个进程进入后,操作后有三个键值对,第二个进程进入后,d,l是一样的,会被覆盖掉,如果把n修改成1就变成三个键值对,
#因为第一个进程进入时,字典是空的,会首先创建三个键值对,第二个进程进入时,会进行覆盖,因为字典是共用的,d[1] = 1都一样,
#所以10个进程走完后,三个键值对不还是一样
    l.append(n)   #往列表中添加值

if __name__=='__main__':
    with Manager() as manager:  ##manager = manager()
        d = manager.dict()   #创建一个空字典实现进程之间共享

        l = manager.list(range(5))  #创建一个从0到4的列表
        p_list = []
        for i in range(10):         #创建10个进程
            p = Process(target=f,args=(d,l,i))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
        print(l)
[root@node2 multiprocess]# python3 manager.py
{0: '1', '2': 2, 0.25: None, 3: '1', 5: '1', 1: '1', 6: '1', 4: '1', 7: '1', 8: '1', 2: '1', 9: '1'} #三组键值对
[0, 1, 2, 3, 4, 0, 3, 5, 6, 1, 4, 7, 8, 2, 9]  ##0,1,2,3,4是初始时就有

分析:主进程和子进程是否一致

[root@node2 multiprocess]# cat manager.py
#!/usr/local/python3/bin/python3
from multiprocessing import Process, Manager
def f(d,l,n):
    d[n] = '1'
    d['2'] = 2
    d[0.25] = None
    l.append(n)
    print('sub',id(d))            #########

if __name__=='__main__':
    with Manager() as manager:
        d = manager.dict()

        l = manager.list(range(5))
        p_list = []

        print('main',id(d))         #######
        for i in range(10):
            p = Process(target=f,args=(d,l,i))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
        print(l)

[root@node2 multiprocess]# python3 manager.py   ##结果windows不一样,linux中是一样的
main 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
sub 140423045960872
{0: '1', '2': 2, 0.25: None, 3: '1', 2: '1', 4: '1', 7: '1', 8: '1', 5: '1', 1: '1', 9: '1', 6: '1'}
[0, 1, 2, 3, 4, 0, 3, 2, 4, 7, 8, 1, 5, 6, 9