一 multiprocessing模块介绍
python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。
二 Process类
构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。
实例方法:
is_alive():返回进程是否在运行。
join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
start():进程准备就绪,等待CPU调度
run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
terminate():不管任务是否完成,立即停止工作进程
属性:
daemon:和线程的setDeamon功能一样
name:进程名字。
pid:进程号。
三 Process类的使用
创建并开启子进程的两种方式
import time import random from multiprocessing import Process def foo(name): print('%s start' %name) time.sleep(random.randrange(1,6)) print('%s end' %name) p1 = Process(target=foo,args=('Tom',)) #必须加 逗号 p2 = Process(target=foo,args=('Jerry',)) p3 = Process(target=foo,args=('Guido',)) if __name__ == '__main__': p1.start() p2.start() p3.start() print('主线程')
import time import random from multiprocessing import Process class Foo(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s start' %self.name) time.sleep(random.randrange(1,6)) print('%s ---> end' %self.name) p1 = Foo('Tom') p2 = Foo('Jerry') p3 = Foo('Guido') if __name__ == '__main__': p1.start() #start会自动调用run p2.start() p3.start() print('主线程')
进程之间的内存空间是隔离的
from multiprocessing import Process n = 100 #在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了 def work(): global n n = 0 print('子进程:',n) if __name__ == '__main__': p = Process(target=work) p.start() print('主进程:',n) #结果: 主进程: 100 子进程: 0
Process对象的join方法
import time import random from multiprocessing import Process class Foo(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s start' %self.name) time.sleep(random.randrange(1,6)) print('%s ---> end' %self.name) p = Foo('Tom') if __name__ == '__main__': p.start() p.join(0.0001) #等待p停止,等0.0001秒就不再等了 print('开始')
import time import random from multiprocessing import Process def foo(name): print('%s start' %name) time.sleep(random.randrange(1,6)) print('%s ---> end' %name) p1 = Process(target=foo,args=('Tom',)) #必须加 逗号 p2 = Process(target=foo,args=('Jerry',)) p3 = Process(target=foo,args=('Guido',)) if __name__ == '__main__': p1.start() p2.start() p3.start() # 疑问:既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗? # 当然不是了,必须明确:p.join()是让谁等? # 很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p, # 详细解析如下: # 进程只要start就会在开始运行了,所以p1-p3.start()时,系统中已经有四个并发的进程了 # 而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键 # join是让主线程等,而p1-p3仍然是并发执行的,p1.join的时候,其余p2,p3仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join直接通过检测,无需等待 # 所以3个join花费的总时间仍然是耗费时间最长的那个进程运行的时间 p1.join() p2.join() p3.join() print('主线程') #上述启动进程与join进程可以简写为 p_l = [p1,p2,p3] for p in p_l: p.start() for p in p_l: p.join()
注意:在windows中Process()必须放到# if __name__ == '__main__':下
Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module. If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources). This is the reason for hiding calls to Process() inside if __name__ == "__main__" since statements inside this if-statement will not get called upon import. 由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。 如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。 这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。
四 守护进程
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
import time import random from multiprocessing import Process class Foo(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s start' %self.name) time.sleep(random.randrange(1,6)) print('%s ---> end' %self.name) p = Foo('Tom') if __name__ == '__main__': p.daemon = True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行 p.start() print('主进程')
#主进程代码运行完毕,守护进程就会结束 from multiprocessing import Process import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") p1=Process(target=foo) p2=Process(target=bar) p1.daemon=True p1.start() p2.start() print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
五 进程同步(锁)
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,
而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理
part1:多个进程共享同一打印终端
#并发运行,效率高,但竞争同一打印终端,带来了打印错乱 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': for i in range(3): p=Process(target=work) p.start() #结果: 16924 is running 14620 is running 8640 is running 8640 is done 14620 is done 16924 is done
#由并发变成了串行,牺牲了运行效率,但避免了竞争 from multiprocessing import Process,Lock import os,time def work(lock): lock.acquire() print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) lock.release() if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start() #结果 11496 is running 11496 is done 13344 is running 13344 is done 11792 is running 11792 is done
part2:多个进程共享同一文件
文件当数据库,模拟抢票
#文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db.txt')) print('