进程概念:一个运行的程序称为进程。每个进程一都有自己的系统状态,包括内存、已打开文件列表,用于跟踪正在执行的指令的程序计数器以及用于函数的局部变量的调用栈。通常在一个控制流序列中进程逐条执行语句,这一般称为进程的主线程。

multiprocessing 类似threading

1)父进程和子进程:

默认:默认情况下主进程等待所有子进程结束后再结束,类似threading的。

[root@VM_131_54_centos multi]# cat m2.py 
#encoding:utf-8
from   multiprocessing import Process
import os
from time import *
def runProcess(name=None):
    sleep(3)
    print '子进程的pid,os.getpid::--->',os.getpid()

if __name__=="__main__":
    p = Process(target=runProcess)
    p.start()#启动进程,这代表着启动进程中的子进程。
    print '主进程pid,:p.pid--->>',os.getpid()

[root@VM_131_54_centos multi]# python m2.py 
主进程pid:---> 17303
子进程的pid,os.getpid::---> 17304

p.is_alive()

如果子进程还在运行,返回True

p.join([timeout])

等待进程p终止,timeout是可选的超时期限。


关于join的一点困惑:我之前理解是子进程超时就把它结束了。但是并不是这样的。。join是用来阻塞当前进程的,p.start()之后,p就提示主线程,需要等待p结束才向下执行,那主线程就乖乖的等着啦,自然没有执行p2.start()这一句啦,当然就变成了图示的效果了。
有使用join()的情况。

[root@VM_131_54_centos multi]# cat m2.py 
#encoding:utf-8
from   multiprocessing import Process
import os
from  time import sleep

def runProcess(name=None):
    print '子进程的pid,os.getpid::--->',os.getpid()
    sleep(10)
    print '1'


if __name__=="__main__":
    p = Process(target=runProcess)
    p.start()
    #p.join(5)  #下面会启动这个join
    p2 = Process(target=runProcess)
    p2.start()

输出:
[root@VM_131_54_centos multi]# python m2.py 
子进程的pid,os.getpid::---> 20557  #
子进程的pid,os.getpid::---> 20558  #这两句话可以认为同时打印的,同步执行。
1  
1  #两个1也是同时打印的。

下面是加入join()的实例:

[root@VM_131_54_centos multi]# python m2.py 
子进程的pid,os.getpid::---> 23300   #打印这个之后过了5秒才有下一句,
子进程的pid,os.getpid::---> 23304
1                 #这个是进程1 打印的,是sleep(10)后打印的结果。过了5秒后,打印下一句
1

从上面可以看出,join()是阻塞进程的。

terminate()

p.terminate()

强制终止进程,如果调用此函数,进程p将被立即终止,同时不会进行任何清理动作,如果进程p创建了自己的子进程,那么那些进程将变成僵尸进程。如果p保存了一个锁或者进程间通信被调用,那么终止它可能导致死锁或I/o崩溃。
Process类的实例 p 具有的方法如下:

p.is_alive()
p.join([timeout])
p.run()
p.start()
p.terminate()

p实例具有的属性:

p.authkey
p.deamon
p.exitcode
p.name
p.pid

进程池

Pool([numprocess,[,initializer[,initargs]]]):
创建工作进程池,numprocess是要创建的进程数。如果省略此参数,将使用cpu_count值,initializer是每个工作进程启动时要执行的可调用对象。initargs是要传递的参数元组。

Pool类的实例p支持一下操作:

p.apply(func[,args[,kwargs]]:在一个池工作进程中执行函数(*args,**kwargs),
    然后返回结果。这里要强调一点:此操作并不会在所有池工作进程中并行执行func函数。
    如果要通过不同参数并发地执行func函数,
    必须从不同线程调用p.apply函数或者使用p.apply_async()函数。
p.appy_async()
[root@VM_131_54_centos multi]# cat m4.py 
#encoding:utf-8
import multiprocessing  
import os
import time



def startWork(i):
    print 'pid---->',os.getpid(),' and iam  -->',i
    time.sleep(1)
    print 'end',i




pool  = multiprocessing.Pool(processes = 3)#这句话的位置必须要注意。

for i in range(5):
    pool.apply_async(startWork, (i, ))

pool.close()#关闭线程池,相当于不能添加新任务
pool.join() #主进程  创建/添加任务后,主进程默认不会等待进程池中的任务执行完后才结束,而是主进程任务做完后直接结束。如果这里没有join,子进程就可能有任务还没没完成,主进程就结束,并程序退出了。


[root@VM_131_54_centos multi]# python m4.py 
pid----> 25533  and iam  --> 0
pid----> 25534  and iam  --> 1
pid----> 25535  and iam  --> 2
end 1
pid----> 25534  and iam  --> 3
end 2
pid----> 25535  and iam  --> 4
end 0
end 4
end 3

首先pool的创建必须是在要执行的函数的下面(这里是startWork函数下面。)相当于:拷贝了一份上面的代码,如果放在开头的话,执行pool.apply_async(startWork, (i, ))的话,就会发现找不到startWork,而报错。
apply_async: 非阻塞
apply : 阻塞式

Queue队列

multiprocessing模块支持进程间通信的两种主要形式:管道和队列。这两种方法都使用了消息传递实现的,但队列接口有意模仿线程程序中常见的队列用法。 
有关Queue编程实例可以查看微博内容。

Queue([maxsize]) 
创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。另外,还需要运行支持线程以便队列中的数据传输到底层管道中。 
Queue的实例q具有以下方法:

q.cancel_join_thread() 
不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。

q.close() 
关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。

q.empty() 
如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。

q.full() 
如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)。。

q.get( [ block [ ,timeout ] ] ) 
返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。

q.get_nowait( ) 
同q.get(False)方法。

q.join_thread() 
连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread()方法可以禁止这种行为。

q.put(item [, block [,timeout ] ] ) 
将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。

q.qsize() 
返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。

JoinableQueue([maxsize]) 
创建可连接的共享进程队列。这就像是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。 
JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:

q.task_done() 
使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。

q.join() 
生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。 
下面的例子说明如何建立永远运行的进程,使用和处理队列上的项目。生产者将项目放入队列,并等待它们被处理。

来自python官方手册的

[root@VM_131_54_centos ~]# cat m4.py 
from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print q.get()    # prints "[42, None, 'hello']"
    p.join()

多进程使用队列:

[root@VM_131_54_centos duojingcheng]# cat m2.py 
#encoding:utf8
from multiprocessing import Process,Queue
import os
import time

def runProcess(q):
    print q.get()

if __name__ == "__main__":
    n = 9
    q = Queue()
    for x in range(10):
        q.put(x)
    for x in range(n):
        p = Process(target=runProcess,args=(q,))
        p.start()
[root@VM_131_54_centos duojingcheng]# python m2.py 
0
1
2
3
4
5
6
7
8
#这里有个问题,如果队列已经取完了,就会一直等待,可以使用带参数的get方法。

pipe管道

pipe([duplex]):双工,在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1和conn2是表示管道两端的
Connection对象。默认情况下,管道是双向的。如果将duplex置为False,conn1只能用于接收,而conn2只能用于发送,
必须在创建和启动使用管道的Process对象之前调用pipe方法。

pipe()方法返回的Connection对象的实例c具有以下方法和属性。

c.close():关闭连接。如果c被垃圾收集,将自动调用方法。
c.fileno():返回连接使用的整数文件描述符。
c.poll([timeout]):如果连接上的数据可用,返回true。timeout指定等待的最长时限。
    如果省略此参数,立即返回结果。
    如果timeout=None,则无限期的等待数据到达。
c.recv():接收c.send方法返回的对象,如果连接的另一端已经关闭,再也不存在任何数据,
    将引发EOFError异常。
c.recv_bytes([maxlength]):接收c.send_bytes方法发送的一条完整的字节消息。
    maxlength指定要接收的最大字节数。
    如果进入的消息超过这个最大值,将引发IOError异常,并且在连接上无法进一步读取。
    如果连接的另一端已经关闭,再也不存在任何数据,将引发EOFError异常。
c.recv_bytes_into(buffer [,offset]):接收一条完整的字节消息,并把它
    保存在buffer对象中,该对象支持可写入的.返回值是收到的字节数。
    如果消息长度长于可用的缓冲区空间,将引发BufferTooShort异常。
c.send(obj):通过连接发送对象,obj是与序列化兼容的任意对象。
c.send_bytes(buffer[,offset[,size]]):通过连接发送字节数据缓冲区。。

示例:

[root@VM_131_54_centos duojingcheng]# cat m4.py 
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,))
    p.start()
    print parent_conn.recv()   # prints "[42, None, 'hello']"
    p.join()

[root@VM_131_54_centos duojingcheng]# python m4.py 
[42, None, 'hello']

共享内存:多进程共享变量。


[root@VM_131_54_centos duojingcheng]# cat m5.py 
#encoding:utf8
from  multiprocessing import Queue,Pool,Process,Value
import time

#每个5s,就往队列中添加元素
def createNumber(q,flag):
    while 1:
        for x in range(20):
            q.put(x)
        print   time.ctime(),"加入完毕"
        time.sleep(5)
        if flag.value != 0:
            print "停止添加队列:flag.value:--",flag.value
            break
        else:
            print flag.value

def runProcess(q):
    pass

if __name__=="__main__":
    flag = Value("i",0)
    print "flag:",flag.value
    q = Queue()
    creater = Process(target=createNumber,args=(q,flag))
    creater.start()
    time.sleep(20)
    flag.value=1



[root@VM_131_54_centos duojingcheng]# python m5.py 
flag: 0
Tue Jan 30 19:28:42 2018 加入完毕
0
Tue Jan 30 19:28:47 2018 加入完毕
0
Tue Jan 30 19:28:52 2018 加入完毕
0
Tue Jan 30 19:28:57 2018 加入完毕
0
Tue Jan 30 19:29:02 2018 加入完毕
停止添加队列:flag.value:-- 1

多进程共享不断更新的队列。

[root@VM_131_54_centos duojingcheng]# cat m6.py 
#encoding:utf8
from  multiprocessing import Queue,Pool,Process,Value
import time,os
#每个10s,就往队列中
def createNumber(q,flag):
    d = []
    for x in range(3):
        y = (x+1)*10
        x = x*10
        d.append(range(x,y))
    for x in d:
        for y in x:
            q.put(y)
        print "i am sleeping"
        time.sleep(4)

def runProcess(q):
    while 1:
        i = q.get(timeout=10)
        time.sleep(0.1)
        print time.ctime(),":",os.getpid(),":",i



if __name__=="__main__":
    flag = Value("i",0)
    q = Queue()

    creater = Process(target=createNumber,args=(q,flag))
    creater.start()
    for x in range(8):
        user = Process(target=runProcess,args=(q,))     
        user.start()
    time.sleep(20)
    flag.value=1

结果如下,q.get(timeout=10),所以最后会因为空队列不更新后,等待10s后异常退出。

i am sleeping
Tue Jan 30 21:48:35 2018 : 30689 : 1
Tue Jan 30 21:48:35 2018 : 30687 : 7
Tue Jan 30 21:48:35 2018 : 30691 : 5
Tue Jan 30 21:48:35 2018 : 30690 : 4
Tue Jan 30 21:48:35 2018 : 30692 : 6
Tue Jan 30 21:48:35 2018 : 30686 : 3
Tue Jan 30 21:48:35 2018 : 30685 : 2
Tue Jan 30 21:48:35 2018 : 30688 : 0
Tue Jan 30 21:48:35 2018 : 30689 : 8
Tue Jan 30 21:48:35 2018 : 30687 : 9
i am sleeping
Tue Jan 30 21:48:39 2018 : 30692 : 12
Tue Jan 30 21:48:39 2018 : 30687 : 17
Tue Jan 30 21:48:39 2018 : 30688 : 15
Tue Jan 30 21:48:39 2018 : 30685 : 14
Tue Jan 30 21:48:39 2018 : 30690 : 11
Tue Jan 30 21:48:39 2018 : 30689 : 16
Tue Jan 30 21:48:39 2018 : 30686 : 13
Tue Jan 30 21:48:39 2018 : 30691 : 10
Tue Jan 30 21:48:39 2018 : 30687 : 19
Tue Jan 30 21:48:39 2018 : 30692 : 18
i am sleeping
Tue Jan 30 21:48:43 2018 : 30689 : 23
Tue Jan 30 21:48:43 2018 : 30692 : 27
Tue Jan 30 21:48:43 2018 : 30687 : 26
Tue Jan 30 21:48:43 2018 : 30686 : 24
Tue Jan 30 21:48:43 2018 : 30691 : 25
Tue Jan 30 21:48:43 2018 : 30690 : 22
Tue Jan 30 21:48:43 2018 : 30685 : 21
Tue Jan 30 21:48:43 2018 : 30688 : 20
Tue Jan 30 21:48:43 2018 : 30692 : 29
Tue Jan 30 21:48:43 2018 : 30689 : 28
Process Process-3:
Traceback (most recent call last):
..............................还有很多报错信息。省略