进程、线程、协程(二) 多进程实现

Python的多线程并没有有效利用多个cpu,只用到一个cpu,为了最大的利用cpu资源和提高效率,一般用多进程去实现。

Python多进程常用包时multiprocessing。包含Process、Queue、Pip、Lock、Pool、Manager、Semaphore、Event等类。

 

下面分别对这些类进行使用和相关用法进行说明:

1、Process

类的构造方式:

def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):

# group:进程所属组,基本不用,可以不传,传的话必须为None
# target:进程调用对象(可以是一个函数名,也可以是一个可调用的对象(实现了__call__方法的类)) 
# args:调用对象的位置参数元组 
# name:给这个进程命名
# kwargs:调用对象的关键字参数字典

实例方法:

is_alive():返回进程是否在运行 ,True或False
start():启动进程,等待CPU调度 
join([timeout]):阻塞当前上下文环境,直到调用此方法的进程终止或者到达指定timeout 
terminate():不管任务是否完成,立即停止该进程 
run():start()调用该方法,当实例进程没有传入target参数,stat()将执行默认的run()方法

 

属性:

authkey: Set authorization key of process。(没用过)
daemon:守护进程标识,在start()调用之前可以对其进行修改 
exitcode:进程的退出状态码,运行中为None,正常退出为0
name:进程名 
pid:进程

 

join(timeout=)的作用就是阻塞当前的主进程,等子进程结束后才开始主进程。要是子进程也阻塞了,那么这个程序就走不动了,所以有timeout选项,子进程没在timeout时间内完成后,主进程就不等了。

例子,当没有join时,主进程并不会等待子进程

from multiprocessing import Process
import os
import time


def f1(value):
    time.sleep(value)
    print "into process pid: %s" % os.getpid()


if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    p1 = Process(target=f1, args=(2,))
    p1.start()
    print "main process end"

结果:
main process start: 13196
main process end
into process pid: 4064

有了join,主进程会等待子进程

from multiprocessing import Process
import os
import time


def f1(value):
    time.sleep(value)
    print "into process pid: %s" % os.getpid()


if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    p1 = Process(target=f1, args=(2,))
    p1.start()
    p1.join()
    print "main process end"
结果:
main process start: 6432
into process pid: 13336
main process end

 

daemon属性默认为False,当设置为True时,主进程将不会等待子进程。

daemon让主进程不等子进程,而join()让主进程阻塞,当两个同时出现时,就算daemon为True也会被join阻塞了,最后还是等待子进程结束。

from multiprocessing import Process
import os
import time


def f1(value):
    time.sleep(value)
    print "into process pid: %s" % os.getpid()


if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    p1 = Process(target=f1, args=(2,))
    p1.daemon = True
    p1.start()
    p1.join()
    print "main process end"
结果:
main process start: 13196
into process pid: 11152
main process end

注意,daemon的设置要在start()之前。

 

2、Pool

当我们要起很多个同样的进程时,Pool就派上用场了,进程池可以生成多个进程。

类的构造方式:

def __init__(self, processes=None, initializer=None, initargs=(),
                 maxtasksperchild=None):

# processes:进程的数量;若processes是None,默认是cpu的数量

# initializer:若initializer是None,则每一个工作进程在开始的时候就会调initializer(*initargs)

# maxtasksperchild:工作进程退出前可以完成的任务数,完成后用一个新的工作进程来替代原进程,让闲置的# 资源释放,maxtasksperchild默认是None,此意味只要Pool存在工作进程就一直存活

 

实例方法

apply_async(func, args=(), kwds={}, callback=None):非阻塞进程,可以同时执行很多个进程,数量跟cpu数有关
apply(func, args=(), kwds={}):阻塞,不管进程多少,只能一个一个执行,等同于单进程
close():关闭pool,不能再添加新的任务
terminate():结束运行的进程,不再处理未完成的任务
join():和Process介绍的作用一样, 但要在close或terminate之后使用

map(func, iterable, chunksize=None):同map

from multiprocessing import Pool
import os
import time


def f1(value):
    time.sleep(value)
    print "into process pid: %s" % os.getpid()
    print time.ctime()


if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    p1 = Pool(processes=5)
    # for i in range(10):
    #     p1.apply_async(f1,(i,))
    p1.map(f1,range(10))
    p1.close()
    p1.join()
    print "main process end"

 map()的效果相当于map和apply_async结合。

 

3、Value Array

Value、Array是通过共享内存的方式共享数据的。

类的构造方式:

def Value(typecode_or_type, *args, lock):

参数解释:
# typecode_or_type:定义共享内存中分配的对象类型,ctype类型或者ctype类型的code。常用ctype的
# code,整数用i, 字符用c,浮点数用d 
# args:传递给ctype的构造参数
# lock:默认值是True。创建一个新的lock来控制对value的访问




def Array(typecode_or_type, size_or_initializer, lock):

参数解释:
# typecode_or_type:定义共享内存中分配的对象类型,ctype类型或者ctype类型的code。常用ctype的
# code,整数用i, 字符用c,浮点数用d 
# size_or_initializer:如果它是一个整数,那么它确定数组的长度,并且数组将被初始化为零。否则,
# size_or_initializer是用于初始化数组的序列,其长度决定数组的长度
# lock:默认值是True。创建一个新的lock来控制对value的访问
from multiprocessing import Value, Array, Process
import os
import time


def f1(v, a):
    print  "into f1"
    v.value += 10.0
    for i in range(len(a)):
        a[i] += 10

if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    v = Value('d', 10.0)
    a = Array('i', range(10))
    p = Process(target=f1, args=(v, a))
    p.start()
    p.join()
    print v.value, a[:]
    print "main process end"

输出:
main process start: 1436
into f1
20.0 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
main process end

4、Manager

Manager是通过共享进程的方式共享数据。Manager共享的数据类型较多,除了以上说的Array和Value,还支持Python所有数据类型以及共享实例。

from multiprocessing import Manager, Process
import os
import time


def f1(v, a, l, d):
    print  "into f1"
    v.value += 10.0
    for i in range(len(a)):
        a[i] += 10
    l.append(v.value)
    d["share"] = a[:]

if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    v = Manager().Value('d', 10.0)
    a = Manager().Array('i', range(10))
    l = Manager().list()
    d = Manager().dict()
    ps = [Process(target=f1, args=(v, a, l, d)) for i in range(3)]
    for p in ps:
        p.start()
    for p in ps:
        p.join()
    print v.value, a[:], l, d
    print "main process end"
输出:
main process start: 14776
40.0 array('i', [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]) [20.0, 30.0, 40.0] {'share': array('i', [30, 31, 32, 33, 34, 35, 36, 37, 38, 39])}
main process end

 

5、Queue

Queue实现进程间通信,它是进程安全的,用队列先进先出的顺序,有序地实现数据在进程间的传递。

 

构造方式:

def Queue(maxsize=0):

#maxsize:队列的长度

实例方法:

put(obj, block=True, timeout=None):将对象obj放入队列。如果block为True,该方法会阻塞timeout值的时间,如果这段时间内,队列都没有空出空间,则抛出Full异常(队列已满异常);如果block为False,队列满了就抛出Full异常。
get(block=True, timeout=None):获取队列里的对象。如果block为True,该方法会阻塞timeout值的时间,如果这段时间内,队列都是空的,则抛出队列enpty异常;如果block为False,队列是空的就抛出异常。
qsize():当前队列中有多少对象。
empty():队列为空,则返回True。
full():队列已满,则返回True。
get_nowait():不等待获取队列中的对象。相当于get(False)。队列为空则抛出Empty异常
put_nowait(obj):不等待将对象放入队列。相当于put(obj,False)。队列满则抛出异常。
close():关闭队列。

用Queue实现生产者消费者模型。

from multiprocessing import Process, Queue
import os
import time


def write(q):
    count = 1
    while True:
        q.put("something")
        print "put someting: %s"  % count
        count += 1
        time.sleep(1)

def read(q):
    while True:
        print q.get()

if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    q = Queue(maxsize=5)
    p1 = Process(target=write, args=(q,))
    p2 = Process(target=read, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print "main process end"

 

6、Pipe

两个进程之间的数据通信。

构造方式:

def Pipe(duplex=True):

参数解释:
duplex:默认为True。若为True,则该管道是全双工的,两端可同时收同时发;若为False,则第一个管道对象只负责收消息,第二个管道对象只负责发消息。

 

 实例方法:

send(obj):发送对象到另一端。

recv():接收另一端调用send()发过来的对象,如果没有对象可以接收,该方法会一致阻塞。如果另一端已经关闭,则会抛出IOError。

close():关闭管道连接。当连接被垃圾回收的时候会调用这个方法。

fileno():Return the file descriptor or handle used by the connection。

poll(timeout=None):如果连接中的数据是可读的,返回True。没有设定timeout将会立即返回结果,time设置为None则会一直等下去直到数据到达。

send_bytes(buffer, fooset=-1, size=-1):通过连接发送字节数据。如果offset给出,则从缓冲区的相应位置读取; 如果size给出, 则从缓冲读取相应字节的数据(数据超过32M可能抛出异常)。

recv_bytes(maxlength=-1):接收send_bytes()方法发送的一条字节消息。在接收前会一直阻塞;如果没有收到任何信息并且另一端关闭的话将会抛出异常;如果maxlength参数给出并且接收的数据长度超出此参数,将抛出异常,并且该连接将不再可读数据。

recv_bytes_into(buffer, offset=-1):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

 

import os
from multiprocessing import Process, Pipe

def f1(pipe):
    pipe.send("p1 first send")
    recv = pipe.recv()
    print "p1 first receive: %s" % recv
    pipe.send("p1 second send")
    recv = pipe.recv()
    print "p1 second receive: %s" % recv

def f2(pipe):
    pipe.send("p2 first send")
    recv = pipe.recv()
    print "p2 first receive: %s" % recv
    pipe.send("p2 second send")
    recv = pipe.recv()
    print "p2 second receive: %s" % recv



if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    p1, p2 =Pipe()
    p1.fileno()
    pro1 = Process(target=f1, args=(p1,))
    pro2 = Process(target=f2, args=(p2,))
    pro1.start()
    pro2.start()
    pro1.join()
    pro2.join()
    print "main process end"

输出结果:
main process start: 12584
p2 first receive: p1 first send
p1 first receive: p2 first send
p1 second receive: p2 second send
p2 second receive: p1 second send
main process end

 

7、Lock

进程加锁,进程间对同一个数据进行操作时,为保护数据安全,同一时间只允许一个进程对数据进行操作。其执行过程类似串行,效率上慢了,安全性高了。

import os
import time
from multiprocessing import Process, Value, Lock

def f1(v, lock):
    lock.acquire()
    print "into f1, the value is : %s" % v.value
    v.value += 1
    print "increase 1 to value: %s" % v.value
    v.value -= 1
    print "decrease 1 to value: %s" % v.value
    time.sleep(2)
    print "left f1, the value is :%s" % v.value
    lock.release()

if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    lock = Lock()
    v = Value("i", 10)
    p_list = [Process(target=f1, args=(v, lock)) for i in range(100)]
    for p in p_list:
        p.start()
    for p in p_list:
        p.join()
    print "main process end"

上述代码正常情况下,进入和离开打印的值都是10,如果不加锁,进入和离开的值就不确定了。

 

8、Semaphore

信号量相当于Lock()的高级版,可以控制锁的数量。信号量Semaphore是一个计数器,控制对公共资源或者临界区域的访问量,信号量可以指定同时访问资源或者进入临界区域的进程数。每次有一个进程获得信号量时,计数器-1,若计数器为0时,其他进程就停止访问信号量,一直阻塞直到其他进程释放信号量。


from multiprocessing import Process,Semaphore
import time
import random
def func(i,sem):
    sem.acquire()
    print('第%s个人进入小黑屋,拿了钥匙锁上门' % i)
    time.sleep(random.randint(3,5))
    print('第%s个人出去小黑屋,还了钥匙打开门' % i)
    sem.release()
if __name__ == '__main__' :
    print "main process start: %s" % os.getpid()
    sem =Semaphore(5)
    for i in range(20) :
        p = Process(target=func,args=(i,sem))
        p.start()
    print "main process end"

 

9、Event

当某一个进程的处理逻辑需要依赖于另一个进程的状态时,这时候需要用事件来同步。

事件的处理机制:全局定义一个Flag,如果Flag为False,当程序执行wait()方法时,就会阻塞。如果Flag为True,那么wait()就不会再阻塞。

实例方法:

is_set():判断Flag的状态。如果Flag为True,那么返回True,否则返回False。

set():将Flag设置为True。

clear():将Flag设置为False。

wait(timeout=None):等待事件timeout值的时间,如果超时则不等待继续往下执行。

import os
import time
from multiprocessing import Process, Event

def tra(e):
    while 1:
        if e.is_set():
            time.sleep(5)
            print 'red light is on!'
            e.clear()
        else:
            time.sleep(5)
            print 'green light is on!'
            e.set()
def Car(i, e):
    e.wait()
    print '%s th car has passed!' % i

if __name__ == "__main__":
    print "main process start: %s" % os.getpid()
    e = Event()
    triff_light = Process(target=tra, args=(e,))
    triff_light.start()
    for i in range(100):
        car = Process(target=Car, args=(i + 1, e,))
        car.start()
        time.sleep(2)
    print "main process end"

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值