多进程
multiprocessing.Process
创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()启动某个进程。
属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。
创建的多个进程是一块执行的
from multiprocessing import Process
import time
import os
def func(str):
print("Process:",str," Pid:", os.getpid())
time.sleep(3)
print("Process:",str, "end"," Pid:", os.getpid())
if __name__ == '__main__':
t1 = time.time()
# 子进程异步,多个进程一起执行
p1 = Process(target=func,args=("apply 0",))
p2 = Process(target=func,args=("apply 1",))
p3 = Process(target=func,args=("apply 2",))
p4 = Process(target=func,args=("apply 3",))
p5 = Process(target=func,args=("apply 4",))
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
print("Main End")
print(time.time() - t1)
进程池
multiprocessing.Pool
开多进程是为了并发,通常有几个cpu核心就开几个进程,但是进程开多了会影响效率,主要体现在切换的开销,所以引入进程池限制进程的数量。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
from multiprocessing import Pool, Process
import time
import os
def func(str):
print("Pool:",str," Pid:", os.getpid())
time.sleep(3)
print("Pool:",str, "end"," Pid:", os.getpid())
if __name__ == '__main__':
p = Pool(3)
t1 = time.time()
# for i in range(4):
# msg = "apply %d"%(i)
# 这里其实类似一个单进程过程,一般情况
# p.apply(func, (msg,))
# apply同步,子进程一个一个执行
# p.apply(func,("apply 0",))
# p.apply(func,("apply 1",))
# p.apply(func,("apply 2",))
# p.apply(func,("apply 3",))
# p.apply(func,("apply 4",))
# apply_async异步,多个子进程一起执行
# p.apply_async(func, ("apply 0",))
# p.apply_async(func, ("apply 1",))
# p.apply_async(func, ("apply 2",))
# p.apply_async(func, ("apply 3",))
# p.apply_async(func, ("apply 4",))
p.close()
p.join()
print("Main End")
print(time.time() - t1)
进程间通信
一、队列 :multiprocessing.Queue
不同于线程queue,进程queue的生成是用multiprocessing模块生成的。
在生成子进程的时候,会将代码拷贝到子进程中执行一遍,及子进程拥有和主进程内容一样的不同名称空间
示例如下:
①主进程和子进程各自拥有的不是同一个队列,之间不能通信
import multiprocessing
def foo():
q.put([11,'hello',True])
print(q.qsize())
q=multiprocessing.Queue() #全局定义一个q进程队列,在产生子进程时候会在子进程里生成,可以指定最大数,限制队列长度
if __name__ == '__main__':
p=multiprocessing.Process(target=foo,args=()) #因为名称空间不同,子进程的主线程创建的q队列,主进程get不到,所以会阻塞住
p.start()
# foo() #主进程执行一下函数就可以访问到了
print(q.get())
②主进程创建了队列,子进程报错,找不到队列q
import multiprocessing
def foo():
q.put([15,'hello',{"a":"b"}])
print(q.qsize())
if __name__ == '__main__':
q = multiprocessing.Queue() #主进程创建一个q进程队列,子进程不会执行该语句
p=multiprocessing.Process(target=foo,args=()) #因为名称空间不同,子进程的主线程找不到q队列,所以会报错提示没有q
p.start()
print(q.get())
# 程序报错,子进程找不到q队列
③主进程创建队列,并作为参数传递给子进程,从而进程间可以通信
import multiprocessing
import os
def foo(argument):
print('子进程', os.getpid())#定义函数处理进程队列
print("父进程",os.getppid())
argument.put([15,'hello',{"a":"b"}])
print(argument.qsize())
q = multiprocessing.Queue() #全局定义一个进程队列,主进程和子进程在运行时都会分别创建一个q队列,但是创建的队列是不同的
print('test', os.getpid()) #主进程和子进程都会运行该语句
if __name__ == '__main__':
x = multiprocessing.Queue() #主进程定义一个进程队列
p=multiprocessing.Process(target=foo,args=(x,)) #主进程把值传给子进程就可以处理了
p.start()
p.join()
print(x.get())
foo(q) #主进程创建q队列,并作为参数传递给foo
print(q.get())#这是主进程调用的q
数据共享
Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
注:进程间通信应该尽量避免使用共享数据的方式
①共享变量
import multiprocessing
import time
import random
# 存取
def deposit(n,lock):
for i in range(10):
time.sleep(random.randint(0,2))
# 获取锁使用权限
lock.acquire()
# 尽量让加锁的粒度最小,不到不得不要轻易加锁
n.value += i
# 释放使用权限
lock.release()
# 取钱
def withdraw(n,lock):
for i in range(10):
time.sleep(random.randint(0,2))
lock.acquire()
n.value -= i
lock.release()
if __name__ == '__main__':
# 使用系统创建的共享变量money它是integer(i), 值2000
money = multiprocessing.Value('i', 2000)
lock = multiprocessing.Lock()
# 创建了两个进程,一个进程存钱,一个进程取钱
d = multiprocessing.Process(target=deposit,
args=(money, lock))
w = multiprocessing.Process(target=withdraw,
args=(money, lock))
# 等待这两个进程执行逻辑,直到执行结束
d.start()
w.start()
time.sleep(3)
print(money.value)
d.join()
w.join()
# 打印最终的结果,如果不是2000,说明银行系统存在漏洞
print(money.value)
②共享数组
import multiprocessing
from datetime import datetime, timedelta
def trans(a, size):
#打印出访问这些数组的具体时间
t = datetime.now()
for i in range(size):
print(a[i])
print("消耗%s"%(datetime.now()-t))
if __name__ == '__main__':
print("Test share Memory")
# 建立起一个在两个进程之间共享的共享内存数组
# 数组类型是整形,大小是10
num = 10
a = multiprocessing.Array('i', num)
# 创建一个进程,把共享数组和长度传递给进程
p = multiprocessing.Process(target=trans,
args=(a,num))
p.start()
③进程池中使用队列
from multiprocessing import Manager, Pool
import time
# Manager().Queue()与Pool一起用;
# multiprocessing.Queue()与Procssess一起用
# 这里的get_nowait,put_nowait和get, put应该是效果一样的,
#因为这个操作的过程太短
def read(q):
for i in range(q.qsize()):
print("read from manager queue:%s"%q.get_nowait())
def write(q):
for i in "ABCDEFG":
time.sleep(2)
q.put_nowait(i)
if __name__ == '__main__':
# Manager大神管理Pool中队列
q = Manager().Queue()
# 通过进程池创建两个进程,一个写,一个读
p = Pool(2)
p.apply_async(write,(q,))
p.apply_async(write,(q,))
p.apply_async(read,(q,))
p.close()
p.join()
print("main end")
④共享数据:列表
from multiprocessing import Manager,Process
def foo(l,i):
l.append(i**i)
if __name__ == '__main__':
man=Manager()
ml=man.list([11,22,33])
l=[]
for i in range(5):
p=Process(target=foo,args=(ml,i))
p.start()
l.append(p)
for i in l: #必须要join,不然会执行报错,处理一个数据必须要一个个来,不能同时处理一个数据
i.join()
print(ml)
⑤共享数据:字典
from multiprocessing import Manager,Process
def foo(d,k,v):
d[k]=v
if __name__ == '__main__':
man=Manager()
md=man.dict({'name':'bob'})
l=[]
for i in range(5):
p=Process(target=foo,args=(md,i,'a'))
p.start()
l.append(p)
for i in l: #必须要join,不然会执行报错,处理一个数据必须要一个个来,不能同时处理一个数据
i.join()
print(md)
详情可参考:
http://www.cnblogs.com/kaituorensheng/p/4445418.html