一、多进程
Python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()
查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing
。
multiprocessing
模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading
的编程接口类似。
multiprocessing
模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process
、Queue
、Pipe
、Lock
等组件。
1、Process类
构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。
一般情况下:只需要传入target和args参数即可。
常用实例方法**:
is_alive()
:返回进程是否在运行。join([timeout])
:阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout
(可选参数)。start()
:进程准备就绪,等待CPU调度run()
:strat()
调用run
方法,如果实例进程时未制定传入target
,这star
执行默认run()
方法。terminate()
:不管任务是否完成,立即停止工作进程
主要属性:
name
:进程名字。
pid
:进程号。
使用Process
类创建多进程的两种方法,方法一:
import os
from multiprocessing
#子进程要执行的代码
def run_proc():
print ('子进程本身的id’, os.getpid()) # os.getpid() 用于获取子进程的id
print('父进程的id’, os.getppid()) # os.getppid() 用于获取父进程的id
If __name__ == '__main__’:
print ('父进程id’ % os.getpid()) # 在父进程中获取父进程的id
for i in range(10):
p = multiprocessing.Process(target = run_proc, args=())
p.start()
p.join()
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process
实例,用star()
方法启动,join()
方法可以等待子进程结束后再往下运行,通常用于进程间同步。
方法二:
import time
from multiprocessing import Process
class MyProcess(Process):
def __init__(self, arg):
super(MyProcess, self).__init__()
self.arg = arg
def run(self):
print('say hi', self.arg)
time.sleep(1)
if __name__ == '__main__':
for i in range(10):
p = MyProcess(1)
p.start()
进程之间默认是不能共用内存的:
li = []
def f1(i):
li.append(i)
print('你好',li)
if __name__ =='__main__':#进程不能共用内存
for i in range(10):
p = Process(target=f1,args=(i,))
p.start()
'''每个进程都创建一个列表,然后添加一个因素进去,
每个进程之间的数据是不能共享的'''
2、Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程。
(1)apply
模块的使用,每个任务是排队执行的
from multiprocessing import Process,Pool
from multiprocessing import Manager
import time
def f1(a):
time.sleep(2)
print(a)
if __name__ =='__main__':
pool =Pool(5)
for i in range(5): # 每次使用的时候会去进程池里面申请一个进程
pool.apply(func=f1,args=(i,))
print('你好') # apply里面是每个进程执行完毕了才执行下一个进程
pool.close() # 执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
pool.join() # 等待进程运行完毕,先调用close函数,否则会出错
(2)apply_async
模块,会比apply
模块多个回调函数,同时是异步的
from multiprocessing import Process,Pool
from multiprocessing import Manager
import time
def Foo(i):
time.sleep(1)
return i+50
def Bar(arg):
print(arg)
if __name__ =='__main__':
pool = Pool(5)
for i in range(10):
'''apply是去简单的去执行,而apply_async是执行完毕之后可以执行一
个回调函数,起提示作用'''
pool.apply_async(func=Foo,args=(i,),callback=Bar)#是异步的
print('你好')
pool.close() # 不执行close会报错,因为join的源码里面有个断言会检验是否执行了该方法
pool.join() # 等待所有子进程运行完毕,否则的话由于apply_async里面daemon是设置为True的,
# 主进程不会等子进程,所欲函数可能会来不及执行完毕就结束了
'''apply_async里面,等函数Foo执行完毕,它的返回结果会被当做参数传给Bar'''
apply
和apply_async
方法的主要区别:
#进程池
p = pool(5)
p.apply # 每个任务是排队执行的,进程.join()
p.apply_async # 每一个任务都是并发的,可以设置回调函数,进程.join(),
# 若无jion(),则进程daemon=True,不等待子进程,主进程结束则全部结束。
3、进程数据共享
方法一:使用Array
Array('i’, range(10))中的'i'参数C语言中的类型:
'c’: ctypes.c_char
'u’: ctypes.c_wchar
'b’: ctypes.c_byte
'B’: ctypes.c_ubyte
'h’: ctypes.c_short
'H’: ctypes.c_ushort
'i’: ctypes.c_int
'I’: ctypes.c_uint
'l’: ctypes.c_long,
'L’: ctypes.c_ulong
'f’: ctypes.c_float
'd’: ctypes.c_double
示例:
from multiprocessing import Process, Array
def f(a):
for i in range(len(a)):
a[i] = -a[i]
if __name__ == '__main__':
arr = Array('i', range(10))
p = Process(target=f, args=(arr,))
p.start()
p.join()
print(arr[:])
运行结果:
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
方法二:使用manager
Manager()
返回的manager
提供list
, dict
, Namespace
, Lock
, RLock
, Semaphore
, BoundedSemaphore
, Condition
, Event
, Barrier
, Queue
, Value
and Array
类型的支持。
from multiprocessing import Process
from multiprocessing import Manager
def f1(i,dic):
dic[i] = 200+i
print(dic.values())
if __name__ =='__main__': # 进程间默认不能共用内存
manager = Manager()
dic = manager.dict() # 这是一个特殊的字典
for i in range(10):
p = Process(target=f1,args=(i,dic))
p.start()
p.join()