多进程
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