首先,什么是进程:进程就是正在运行的程序,或程序的运行过程,这是一个高度总结的概念。
串行:一个程序完完整整的运行完毕,然后运行下一个程序。 #记住是完完整整的运行完毕
并发:并发其实就是我们人眼看起来是同时运行的。 #单核计算机可以实现并发,利用cpu的多道技术
并行:就是同一时间,运行着多个进程 #并行只有在多个cpu的计算机,才可以实现。
开启子进程的两种方式
方式一: from multiprocessing import Process def task(n): print(n) if __name__ == '__main__': p1 = Process(target = task,args(5,)) #记住args后面跟着是元组,如果只有一个参数的话,记得加个逗号 p1.start() #这里只是向操作系统发出一个启动一个进程的请求,并不是立马就造出一个进程 方式二: from multiprocessing import Process class Myprocess(Process) #记得要继承Process def run(self): #这里必须是 run,名字不能改 pass if __name__ == '__main__': p1 = Myprocess() p1.start() #相当于是p1.run()
孤儿进程和僵尸进程
当运行父程序,产生一系列的子进程,即使父进程的代码运行完毕,父进程也不会结束,他会等待产生的子进程全部运行完毕,统一回收子进程的僵 尸状态,然后再运行完毕。
僵尸进程是指:子进程运行完毕后,之后把其占用的内存空间释放掉,但是会留下其pid号和占用使用内存的时间等数据,pid便于父进程去查看子 进程的运行状态,等着全部子进程运行完毕后,统一回收其僵尸状态。
孤儿进程是指:假如子进程没有运行完毕,其父进程被意外终止掉了,这样的话,这些子进程运行完毕后,就会变成孤儿进程。其实孤儿进程和僵尸 进程的差别就是一个是有父进程,另一个是没有孤儿进程的。孤儿进程最终也会操作系统回收掉。
总结:孤儿进程是无害的,因为它最终会有人给他回收资源。
僵尸进程大部分情况也是无害的,因为他有他的父进程去回收其资源。但是如果内存中有大量的僵尸进程没有被清理,很有可能就是它的父进程一直 在运行,一直在产生没有意义的子进程,这样的会占用内存中的pid号,导致有软件不能打开。这种情况杀死僵尸进程是没有太大意义的,我们需要 去杀死掉其父进程,这样僵尸进程变成孤儿进程,自然会有人清理这些孤儿进程。
守护进程:
1 守护进程相等于也是一个’子进程‘,是由父进程产生。
2 守护表示伴随的意思,当父进程运行完其代码后,就会跟着父进程一起完毕,不管其守护进程代码到底运行完了没有。
3 守护进程中不能再开一个子进程。
from multiprocessing import Process import time def task(): time.sleep(1) print('我是守护进程') def fbb(): print('我是子进程') if __name__ == '__main__': p1 = Process(target=fbb) p2 = Process(target=task) p2.daemon = True #daemon 默认的是False,想要他变成守护进程,把daemon设置成True,必须在向操作系统发出建立新进程请去之前设置 p1.start() p2.start() p1.join() #p1子进程运行完毕才会父程序的下一行代码 print('父进程')
from multiprocessing import Process import time, os, random def task(): print('%s is running..............' % os.getpid()) # 通过os模块的功能能查看到当前进程的pid号 os.getpid() time.sleep(random.randint(1, 3)) # 查看父进程的pid号 os.getppid() print('%s is done ................' % os.getpid()) if __name__ == '__main__': p1 = Process(target=task) p1.start() print(p1.pid) # 在p1发出start()请求的时候,操作系统就给p1的pid给了一个id号,pid相对于p1的一个数据属性 p1.join() # p1子进程运行完毕,操作系统就就回收了p1子程序在操作系统上的pid,所以说通过tasklist并不能查找到该pid的程序 print(p1.pid) # 这条命令会打印出p1的pid号,这是p1的一个数据属性,就算操作系统回收了操作系统上的pid,但是p1的pid属性是不会发送变化的,但是p1这个属性是没有意义的 print('父程序')
# from multiprocessing import Process # import time, os,random # # # def task(): # print('%s is running..............' % os.getpid()) # time.sleep(random.randint(1,3)) # print('%s is done ................' % os.getpid()) # # # if __name__ == '__main__': # p1 = Process(target=task) # p2 = Process(target=task) # p3 = Process(target=task) # # p1.start() #start()是向操作系统发出新建一个进程的请求,并不是马上就造出一个进程,什么时候是操作说的算的 # p2.start() # p3.start() # # p1.join() #p1,p2,p3是同时的运行的,等待的是父程序,p2,p3并不用等待 # p2.join() # p3.join() # # # # print('父进程') # # 利用for循环去除重复的代码 # from multiprocessing import Process # import time, os,random # # # def task(): # print('%s is running..............' % os.getpid()) # time.sleep(random.randint(1,5)) # print('%s is done ................' % os.getpid()) # # # if __name__ == '__main__': # l = [] # for i in range(10): # p = Process(target=task) # l.append(p) # p.start() # print(p.pid) # for a in l: # a.join() # # # # print('父进程') # # # # p.join() 是让父程序在那里等待p子程序运行完毕后,才开始执行下一行代码,并且回收p子程序的僵尸状态
# from multiprocessing import Process, Lock # import os, json, time, random # # # def select(): # time.sleep(random.randint(1, 3)) # with open('a.txt', 'r', encoding='utf-8') as f: # dic = json.load(f) # print('%s 查看余票 %s' % (os.getpid(), dic['count'])) # # # def buy(): # with open('a.txt', 'r', encoding='utf-8') as f: # dic = json.load(f) # time.sleep(random.randint(1, 2)) # if dic['count'] > 0: # dic['count'] -= 1 # with open('a.txt', 'w', encoding='utf-8') as f: # json.dump(dic, f) # print('%s 购票成功' % os.getpid()) # # else: # print('%s 购票失败' % os.getpid()) # # # def task(mutex): # select() # # mutex.acquire() # buy() # mutex.release() # # # if __name__ == '__main__': # mutex = Lock() # for i in range(10): # p = Process(target=task,args=(mutex,)) # p.start() # 写一个买票的小程序 from multiprocessing import Process,Lock import json, os, time, random def select(): # 查看余票 time.sleep(random.randint(1, 3)) with open('a.txt', 'r', encoding='utf-8') as f: dic = json.load(f) print('%s 查看剩余票为:%s' % (os.getpid(), dic['count'])) def buy(): # 购票 with open('a.txt', 'r', encoding='utf-8') as f: dic = json.load(f) time.sleep(random.randint(1, 3)) if dic['count'] > 0: dic['count'] -= 1 with open('a.txt', 'w', encoding='utf-8') as f: json.dump(dic, f) print('%s 购票成功' % os.getpid()) else: print('%s 购买失败,票卖完了' % os.getpid()) # def task(): # select() # buy() # # # if __name__ == '__main__': # for i in range(10): #模拟10个人进行买票的过程 # p = Process(target=task) # p.start() # 分析 如果多进程去执行task()这个买票的过程,那么其中查看余票和买票的环节都是并发执行的,在查看余票环节用并发实行是没有问题,但是在购票环节 呢,我们看到是一张卖给了五个人,这样是完全违背现实生活的。 # 总结一点:我们对数据的查看操作是完全可用并发去执行,但是对一个数据进行修改时,不能用用并发执行,这样数据就会变得混乱。所以对数据进行篡改操作 必须要一个人改完后,另外一个才可以去进行修改。相对于串行吧。 #解决上面的问题multiprocessing 里面有一个LOCk的类,可以让某一段代码只能用串行去执行,这样保证了数据的安全性 def task(lock): select() lock.acquire() #开启互斥锁 buy() lock.release() #关闭互斥锁 开启互斥锁后,必须要关闭 buy()必须串行执行 #互斥锁一个非常谨慎,开发项目中用的时候要谨慎 if __name__ == '__main__': lock = Lock() #创建一个锁的对象 for i in range(10): p = Process(target=task,args=(lock,)) #args 后面必须时元组形式 p.start()
# 进程间通信(IPC)的两种方式: # 1 PIPE(管道) # 2 Queue(管道 + 互斥锁) 队列 from multiprocessing import Queue q = Queue() # Queue()里面可以规定队列可以装多少个数据 使用队列一般放小一些的数据 q.get() # 在队列中取一个 block=True, timeout=None 锁是默认开启的,阻塞的时间也没有限制 如果q里面没有值,会报错 q.put('first') # 在队列中放一个 block=True, timeout=None 锁是默认开启的,阻塞的时间也没有限制 如果q里面放满了,会报错 # 队列:先进先出
# 生产者消费者模型: # 生产者:在程序中表示产生数据的任务 # 消费者:在程序中表示处理数据的任务 # 生产者————>(某种中间介质:队列)<————消费者 # 生产者消费者模型:平衡生产者与消费者之间的工作能力,从而提高程序整体处理数据的速度 # 例子1: # from multiprocessing import Process, Queue # import time, random # # # def producers(name, food, q): # for i in range(4): # res = '%s%s' % (food, i) # time.sleep(random.randint(1, 3)) # q.put(res) #往q队列里一直放 # print('%s 生产了 %s' % (name, res)) # # # def consumption(name, q): # while True: # time.sleep(random.randint(1, 3)) # res = q.get() #从q队列中一直取 # print('%s 吃了 %s' % (name, res)) # # # if __name__ == '__main__': # q = Queue() # # 生产者 # p1 = Process(target=producers, args=('wy', '冰渴落', q)) # p2 = Process(target=producers, args=('cyp', '大脚板', q)) # p3 = Process(target=producers, args=('yjp', '正新鸡排', q)) # # 消费者 # c1 = Process(target=consumption, args=('zh', q)) # c2 = Process(target=consumption, args=('dsb', q)) # # c1.start() # c2.start() # p1.start() # p2.start() # p3.start() # # print('主进程') # 对例子进行分析:从结果可以看出一直运行到最后,父进程还是没有停止,但父程序的代码已经执行完毕,那原因就出在子进程那里,子进程的程序没有执行完 毕,所以父进程就不能结束。最后得知:生产者生产完毕了,生产者的子进程代码已经运行完毕。但是消费者还在那里等着从q队列中取值,所以消费者子进程 一直没有运行完毕,父进程也在等着消费者子进程运行完,所以父程序就不能关闭。 #
# 根据例子1,我们想到的解决方法就是,生产者生产完毕后,再给消费者发送一个结束信息,消费者收到后,就结束子进程,这样的话,父进程也能关闭。 # 例子2 # from multiprocessing import Process, Queue # import time, random # def producers(name, food, q): # for i in range(4): # res = '%s%s' % (food, i) # time.sleep(random.randint(1, 3)) # q.put(res) # 往q队列里一直放 # print('%s 生产了 %s' % (name, res)) # q.put(None) # 在生产者生产完毕后,发送一个结束信息 # # # def consumption(name, q): # while True: # time.sleep(random.randint(1, 3)) # res = q.get() # 从q队列中一直取 # if res is None: break # 消费者判断收到结束信息,收到就break掉 # print('%s 吃了 %s' % (name, res)) # # # if __name__ == '__main__': # q = Queue() # # 生产者 # p1 = Process(target=producers, args=('wy', '冰渴落', q)) # p2 = Process(target=producers, args=('cyp', '大脚板', q)) # p3 = Process(target=producers, args=('yjp', '正新鸡排', q)) # # 消费者 # c1 = Process(target=consumption, args=('zh', q)) # c2 = Process(target=consumption, args=('dsb', q)) # # c1.start() # c2.start() # p1.start() # p2.start() # p3.start() # # print('主进程') # 加了一个结束信号,子进程和父进程能顺利执行完毕。但是有了一个新问题,就是有一些生产者生产食物快,导致发送了一个结束信号,但同时其他生产者还在 生产食物,所以一个消费者取到结束信号,就停止了,但是此时生产者生产的食物还没有结束,导致消费者并没有把生产者产生的食物吃完。 # 根据前面例子的分析: 1 生产者生产完毕要发生一个结束信息 # 2 必须要生产者全部生产完毕后,才能发这个结束信息。 #例子3 # from multiprocessing import Process, Queue # import time, random # # # def producers(name, food, q): # for i in range(4): # res = '%s%s' % (food, i) # time.sleep(random.randint(1, 3)) # q.put(res) # 往q队列里一直放 # print('%s 生产了 %s' % (name, res)) # def consumption(name, q): # while True: # time.sleep(random.randint(1, 3)) # res = q.get() # 从q队列中一直取 # if res is None: break # 消费者判断收到结束信息,收到就break掉 # print('%s 吃了 %s' % (name, res)) # # # if __name__ == '__main__': # q = Queue() # # 生产者 # p1 = Process(target=producers, args=('wy', '冰渴落', q)) # p2 = Process(target=producers, args=('cyp', '大脚板', q)) # p3 = Process(target=producers, args=('yjp', '正新鸡排', q)) # # 消费者 # c1 = Process(target=consumption, args=('zh', q)) # c2 = Process(target=consumption, args=('dsb', q)) # # c1.start() # c2.start() # p1.start() # p2.start() # p3.start() # # p1.join() # p2.join() # p3.join() # q.put(None) #等p1,p2,p3全部运行完毕,然后在加入结束信息,这样队列最后是结束信息,消费者收到结束信息就结束消费者进程 # q.put(None) # print('主进程') # 上面那个例子有个缺陷,就是我们提前已经知道消费者有了几个,根据这样我们就加了几个结束信息,但实际上,我们不应该知道。 # 终极版本的 from multiprocessing import Process, JoinableQueue import time, random # def producers(name, food, q): # 生产者 # for i in range(3): # res = '%s%s' % (food, i) # time.sleep(random.randint(1, 3)) # q.put(res) # print('%s 生产 %s' % (name, res)) # # # def consumption(name, q): # 消费者 # while True: # time.sleep(random.randint(1, 3)) # res = q.get() # print('%s 吃了 %s' % (name, res)) # q.task_done() def producers(name, food, q): for i in range(3): res = '%s%s' % (food, i) time.sleep(random.randint(1, 3)) q.put(res) print('%s 生产了 %s' % (name, res)) def consumption(name, q): while True: time.sleep(random.randint(1, 3)) res = q.get() print('%s 吃了 %s' % (name, res)) q.task_done() if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=producers, args=('wy', '冰渴落', q)) p2 = Process(target=producers, args=('cyp', '大脚板', q)) p3 = Process(target=producers, args=('yjp', '正新鸡排', q)) c1 = Process(target=consumption, args=('zh', q)) c2 = Process(target=consumption, args=('dsb', q)) c1.daemon = True c2.daemon = True p1.start() p2.start() p3.start() c1.start() c2.start() p1.join() p2.join() p3.join() q.join() print('主进程')