#创建进程的方式一 from multiprocessing import Process import time def task(name): print('%s is running'%name) time.sleep(2) print('%s is over'%name) ''' # 注意:在window系统里,创建进程会将代码以模块的方式从头到尾加载一遍 # 创建进程的代码块要写在 if __name__ == '__main__': 里面,避免出错 # 强调:函数名一旦加括号,执行优先级最高,会马上执行。 ''' if __name__ == '__main__': p = Process(target=task,args=('michael',)) #这句话实例了一个Process对象 p.start() #告诉操作系统创建一个进程 print('我是主进程!') #实际输出的结果:我是主进程! #michael is running #michael is over #解释:操作系统创建进程的时间比代码运行的速度要慢,所以先将主进程的输出结果打印出来 #创建进程的方式二 from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name #必须写run方法 def run(self): print('%s is running'%self.name) time.sleep(2) print('%s is over'%self.name) if __name__ == '__main__': obj = MyProcess('michael') obj.start() print('我是主进程!')
join方法 from multiprocessing import Process import time def task(name,n): print('%s is running'%name) time.sleep(n) print('%s is over'%name) # if __name__ == '__main__': # p1 = Process(target=task,args=('egon1',1)) # p2 = Process(target=task,args=('egon2',2)) # p3 = Process(target=task,args=('egon3',3)) # start_time = time.time() #系统创建进程的时间 # p1.start() #这行代码只是告诉操作系统需要创建进程 # p2.start() # p3.start() # p1.join() #join的作用: 让主进程等待子进程结束,然后再运行主进程,并不会影响子进程的运行 # p2.join() # p3.join() # print('我是主进程',time.time()-start_time) #记录主进程等待的时间 # 上面冗余的代码可以简化 if __name__ == '__main__': start_time = time.time() p_list = [] for i in range(3): p = Process(target=task,args=('子进程%s'%i,i)) p.start() p_list.append(p) for p in p_list: p.join() print('主进程',time.time()-start_time)
#进程间数据隔离 from multiprocessing import Process x = 100 def task(): global x x = 0 if __name__ == '__main__': p = Process(target=task) p.start() p.join() #加上join 为了确保先运行子进程 print('主进程',x) #主进程 100 #结果说明:每个进程之间的数据是彼此隔离的!
#进程对象其他相关方法 from multiprocessing import Process import time import os def task(): print('子进程自己的pid',os.getpid()) time.sleep(3) print('子进程父级的pid',os.getppid()) #is_alive的使用 判断子进程是否存活 if __name__ == '__main__': p = Process(target=task) p.start() print(p.is_alive()) #True 此时的子进程是存在的 p.join(4) #join()里的参数需要大于sleep的参数,不然还是会自动运行,当没有参数时,默认等子进程执行完 print(p.is_alive()) #False is_alive() 放在join后会自动将子进程销毁,因此不存在 print('我是主进程') print(p.pid) #查看子进程自己的pid # termininate的使用 杀死子进程 if __name__ == '__main__': p = Process(target=task) p.start() p.terminate() #关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活 print(p.is_alive()) #True p.join() print('我是主进程!') print(p.is_alive()) #False
#守护进程 主进程创建守护进程: 特点1:守护进程会在主进程代码执行结束后就终止 特点2:守护进程内无法再开启子进程,否则会抛出异常 例子1: from multiprocessing import Process import time def task(name): print('%s正活着'%name) time.sleep(2) print('%s正在死亡'%name) if __name__ == '__main__': p = Process(target=task,args=('子进程',)) p.daemon = True #一定要在p.start前设置,设置子线程p为主线程的守护进程,禁止p创建子线程 #并且父进程代码执行结束,p也将终止运行,由于操作系统的处理时间较慢,正常情况下都是直接执行完父进程就结束了 p.start() # p.daemon = True 将daemon放在 start 后面会报错! print('主进程正在死亡') 例子2: from multiprocessing import Process from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(1) print("end456") if __name__ == '__main__': p1=Process(target=foo) p2=Process(target=bar) p1.daemon=True p1.start() p2.start() # p1.join() #当加上了join后,子进程会正常执行,此时的daemon好像不起作用了!下面有详解! print("main-------")
#join 与daemon之间的关系 1 Python 默认参数创建线程后,不管主线程是否执行完毕,都会等待子线程执行完毕才一起退出,有无join结果一样 2 如果创建线程,并且设置了daemon为true,即thread.setDaemon(True), 则主线程执行完毕后自动退出, 不会等待子线程的执行结果。而且随着主线程退出,子线程也消亡。 3 join方法的作用是阻塞,等待子线程结束,join方法有一个参数是timeout,即如果主线程等待timeout, 子线程还没有结束,则主线程强制结束子线程。 4 如果线程daemon属性为False, 则join里的timeout参数无效。主线程会一直等待子线程结束。 5 如果线程daemon属性为True, 则join里的timeout参数是有效的, 主线程会等待timeout时间后,结束子线程。 此处有一个坑,即如果同时有N个子线程join(timeout),那么实际上主线程会等待的超时时间最长为 N timeout, 因为每个子线程的超时开始时刻是上一个子线程超时结束的时刻。
#僵尸进程与孤儿进程 '''回收子进程的pid等信息的两种方式: 1、父进程正常结束 2、join方法 '''
互斥锁 #模拟抢票 from multiprocessing import Process,Lock import json import time import random def search(i): with open('info','r',encoding='utf-8') as f: data = json.load(f) print('剩余票数为%s'%data.get('ticket')) def buy(i): #买票前还需要查看是否还有票! with open('info','r',encoding='utf-8')as f: data = json.load(f) #模拟网络延迟 time.sleep(random.randint(1,3)) if data.get('ticket') > 0: data['ticket'] -= 1 #买票,库存减一 #将更新的信息重新写入文件 with open('info','w',encoding='utf-8')as f: json.dump(data,f) print('用户%s抢票成功'%i) else: print('没有余票,用户%s抢票失败!'%i) def run(i,mutex): search(i) #所有的进程查询的余票数为一致的,大家的起点一致 这一步应该放入到mutex.acquire下 mutex.acquire() #抢锁 一把锁只能被一个人抢到,其他人等待锁被释放 buy(i) mutex.release() #释放锁,其他人又处于同一地步了 if __name__ == '__main__': mutex = Lock() for i in range(10): p = Process(target=run,args=(i,mutex)) p.start() #产生10个子进程
互斥锁补充:不要随便使用 特点:牺牲了效率但是保证了数据的安全,解决了多个进程操作同一分数据,造成数据不安全的情况。 锁一定要在主进程中创建,给子进程使用: 加锁会将并发变成串行 锁通常用在对数据操作的部分,并不是对进程全程加锁
#小点: from multiprocessing import Process import time, os def task(): print('%s is running' % os.getpid()) # time.sleep(3) if __name__ == '__main__': p = Process(target=task) p.start() print(p.is_alive()) #True p.join() # 等待进程p结束后,join函数内部会发送系统调用wait,去告诉操作系统回收掉进程p的id号 print(p.pid) # ???此时能否看到子进程p的id号 可以看到 print(p.is_alive()) #False print('主') print(p.pid) #依然存在 ''' 上述问题的解释: p.join()是向操作系统发送请求,告知操作系统 子进程p的id号不需要再占用了,回收就可以, 此时在父进程内还可以看到p.pid,但此时的p.pid是一个无意义的id号,因为操作系统已经将该编号回收 '''