python多进程基础详解
作者:七层汉堡王
这篇文章主要介绍了Python多进程基础,结合实例形式分析了Python多进程原理、开启使用进程、进程队列、进程池等相关概念与使用方法,需要的朋友可以参考下
进程
什么是进程
进程指的是一个程序的运行过程,或者说一个正在执行的程序
所以说进程一种虚拟的概念,该虚拟概念起源操作系统
一个CPU 同一时刻只能执行一件事
开启一个进程
from multiprocessing import Process import time def task(name): print('%s is running'%name) time.sleep(3) print('%s is done'%name) # 开启子进程的操作必须放到 # if __name__ == '__main__'的子代码中 # 子进程不会再次加载 if __name__ == '__main__': p=Process(target=task,args=('小王',)) # p=Process(target=task,kwargs={'name':'小王'}) # print(p) p.start() # 主进程只是向操作系统发送了一个开启子进程的信号 # p.start() # 1.操作系统先申请内存空间 # 2.把主进程的数据拷贝到子进程里面 # 3.调用cup才能运行里面的代码 # 创造进程的开销大 print('主')
JOIN方法
当前进程jion别的进程。当前进程就会等到别的进程执行完毕了才会继续开始往下执行
from multiprocessing import Process import time def task(name, n): print('%s is running' % name) time.sleep(n) print('%s is done' % name) if __name__ == '__main__': start = time.time() p_l = [] for i in range(1, 4): p = Process(target=task, args=('小王%s' % i, i)) p_l.append(p) p.start() # 主进程等待子进程 for p in p_l: p.join() print('主', (time.time() - start))
进程之间空间隔离
from multiprocessing import Process # 这个n是主进程里面的值 n = 100 def task(): global n # 改的是子进程里面的全局变量 # 主进程里面没有改 n = 0 if __name__ == '__main__': p=Process(target=task) p.start() p.join() print(n)
进程的常用方法
current_process 查看pid(进程id)
# 1. 进程pid:每一个进程在操作系统内都有一个唯一的id号,称之为pid from multiprocessing import Process, current_process import time def task(): print('%s is running' % current_process().pid) time.sleep(3) print('%s is done' % current_process().pid) # 开启子进程的操作必须放到 # if __name__ == '__main__'的子代码中 # 子进程不会再次加载 if __name__ == '__main__': p = Process(target=task) p.start() print('主', current_process().pid)
os.getpid() 查看进程id
# os模块也可以 from multiprocessing import Process, current_process import time, os def task(): print('%s is running 爹是%s' % (os.getpid(), os.getppid())) time.sleep(3) print('%s is done爹是%s' % (os.getpid(), os.getppid())) # 开启子进程的操作必须放到 # if __name__ == '__main__'的子代码中 # 子进程不会再次加载 if __name__ == '__main__': p = Process(target=task) p.start() # 谁把主进程创造出来的 # 用pycharm就是pycharm创造的 print('主%s爹是%s' % (os.getpid(), os.getppid()))
进程其他方法和属性
from multiprocessing import Process,current_process import time,os def task(): print('%s is running 爹是%s'%(os.getpid(),os.getppid())) time.sleep(30) print('%s is done爹是%s'%(os.getpid(),os.getppid())) # 开启子进程的操作必须放到 # if __name__ == '__main__'的子代码中 # 子进程不会再次加载 if __name__ == '__main__': p=Process(target=task) p.start() # 谁把主进程创造出来的 # 用pycharm就是pycharm创造的 # 进程的名字 print(p.name) # 杀死子进程 p.terminate() # 需要时间 time.sleep(0.1) # 判断子进程是否存活 print(p.is_alive()) print('主%s爹是%s'%(os.getpid(),os.getppid()))
守护进程
本质就是一个"子进程",该"子进程"的生命周期<=被守护进程的生命周期
当被守护的进程执行完了。它也会被杀死
# 主进程运行完了,子进程没有存在的意义 # 皇帝和太监不是同生,但是是同死 from multiprocessing import Process import time def task(name): print('%s活着'%name) time.sleep(3) print('%s正常死亡'%name) if __name__ == '__main__': p1=Process(target=task,args=('老太监',)) # 声明子进程为守护进程 p1.daemon = True p1.start() time.sleep(1) print('皇帝正在死亡')
互斥锁
进程之间内存空间互相隔离,怎样实现共享数据
进程之间内存数据不共享,但是共享同一套文件系统,所以访问同一个文件,是没有问题的,
而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理
''' 抢票 查票 购票 互斥锁: 在程序中进行加锁处理 必须要释放锁下一个锁才能获取,所以程序在合适的时候必须要有释放锁 所以用文件来处理共享数据 1.速度慢 2.必须有互斥锁 ''' import json import time,random from multiprocessing import Process,Lock # 查票 def search(name): with open('db.json','rt',encoding='utf-8')as f: dic = json.load(f) # 模拟查票时间 time.sleep(1) print('%s 查看到余票为 %s'%(name,dic['count'])) # 购票 # 第二个get子进程不会是第一个get子进程修改后count的结果 # 加互斥锁,把这一部分并发变成串行, # 但是牺牲了效率,保证了数据安全 def get(name): with open('db.json','rt',encoding='utf-8')as f: dic = json.load(f) if dic['count']>0: dic['count']-=1 time.sleep(random.randint(1,3)) with open('db.json', 'wt', encoding='utf-8')as f: json.dump(dic,f) print('%s 购票成功'%name) else: print('%s 查看到没有票了'%name) def task(name,mutex): # 并发 search(name) # 串行 # 加互斥锁 mutex.acquire() get(name) # 释放互斥锁 mutex.release() # if __name__ == '__main__': # for i in range(10): # p=Process(target=task,args=('路人%s'%i,)) # p.start() # # join只能将进程的任务整体变成串行 # # 互斥锁可以局部串行 # p.join() # # 数据安全,是指读的时候无所谓,写的(改的)时候必须安全 # # 写的时候是串行,读的时候并发 # 加锁 if __name__ == '__main__': # 主进程加锁 mutex=Lock() for i in range(10): # 锁传入子进程 p=Process(target=task,args=('路人%s'%i,mutex)) p.start() # join只能将进程的任务整体变成串行 # 互斥锁可以局部串行 # p.join() # 数据安全,是指读的时候无所谓,写的(改的)时候必须安全 # 写的时候是串行,读的时候并发
db.json 中只有10张票。如果没有加锁。则可能会出现票呗多卖的情况
进程间通信(IPC机制)
''' 速度快 锁问题解决 ipc机制 进程彼此之间互相隔离,要实现进程间通信(IPC), multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的 共享内存空间 队列=管道+锁 ''' from multiprocessing import Queue # 占用的内存,最好小数据,消息数据,下载地址 # Queue(限制队列里面的个数) # 先进先出 q=Queue(3) # 添加 q.put('a') q.put('b') q.put({'x':2}) print('篮子满了') # 队列满了,相当于锁了 # q.put({'x':2}) # 提取 print(q.get()) print(q.get()) print(q.get()) # # 队列为空,等待加入,也会阻塞,相当于锁了 print('队列为空') print(q.get())
队列被取完了 后面的q.get() 会阻塞直到有新的元素。所以程序不会结束
JoinableQueue 来实现生产消费者
JoinableQueue#task_done()方法当队列里面没有元素会结束线程
''' 小王和小周每人生产10分包子和土豆丝 小戴和小杨一直吃,当队列里面没有食物时。终结进程 ''' import time, random from multiprocessing import Process, JoinableQueue def producer(name, food, q): for i in range(10): res = '%s%s' % (food, i) # 模拟生产数据的时间 time.sleep(random.randint(1, 3)) q.put(res) print('厨师%s生成了%s' % (name, res)) def consumer(name, q): while True: # 订单都没了还在等,队列里面空了 res = q.get() # 模拟处理数据的时间 time.sleep(random.randint(1, 3)) print('吃货%s吃了%s' % (name, res)) # 1每次完成队列取一次,往q.join() ,取干净了q.join()运行完 q.task_done() # 多个生产者和消费者 if __name__ == '__main__': q = JoinableQueue() # 生产者 p1 = Process(target=producer, args=('小王', '包子', q)) p3 = Process(target=producer, args=('小周', '土豆丝', q)) # 消费者 c1 = Process(target=consumer, args=('小戴', q)) c2 = Process(target=consumer, args=('小杨', q)) # #3.守护进程的作用: 主进程死了,消费者子进程也跟着死 # #把消费者变成守护进程 c1.daemon = True c2.daemon = True p1.start() p3.start() c1.start() c2.start() p1.join() p3.join() # 2消费者task_done给q.join()发信号 q.join() print('主') # 生产者运行完?1,2 # 消费者运行完?1,2
当队列为空时,不会傻傻等待而是结束进程
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!