- 创建进程的多方法
- multiprocess模块
- join方法
- 进程间数据默认隔离
- 进程间通信(IPC机制)
- 生产者消费者模型
- 进程相关方法
- 守护进程
- 僵尸进程与孤儿进程
- 互斥锁
- 今日作业
-
本质:
但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如扫地机器人,一旦启动,所有的进程都已经存在
导入模块:from multiprocessing import Process
-
multiprocess不是一个模块是一个操作、管理进程的包。这个包几乎包含了和进程有关的所有子模块。大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
-
Process类介绍
Process(group,target,name,args,kwargs)
强调:
1.需要使用关键子的方式来指定参数
2.args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
1.group参数未使用,值始终未None
2.target表示调用对象,即子进程要执行的任务
3.args表示调用对象的位置参数元组,args=(1,2,‘y’,)
4.kwargs表示调用对象的字典,kwargs={‘name’:‘kkk’}
5.name为子进程的名称 -
Process类创建进程的两种方式
方法一
from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作) # task('jason') # 普通的函数调用是同步操作 print('主进程')
方法二
import time class MyProcess(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print(f'{self.name}正在运行') time.sleep(5) print(f'{self.name}运行结束') if __name__ == '__main__': obj = MyProcess('jason') obj.start() print('主进程')
注意:
join方法强调:在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ == '__main__' 判断保护起来,import 的时候 ,就不会递归运行了。
-
作用
在进程中可以阻塞主进程的执行, 直到等待子线程全部完成之后, 才继续运行主进程后面的代码
其实就是实现进程同步 -
举例说明
'''不加join方法''' from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作) print('主进程') ''' 输出顺序: 主进程 jason正在运行 jason运行结束 '''
'''使用了join的方法''' from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作) p.join() # 先执行完子线程,再往下执行 print('主进程') ''' 输出顺序: jason正在运行 jason运行结束 主进程 '''
使用join方法的时候,一定要看准join的执行位置 以及多任务情况下等待的目标
进程间数据默认隔离内存可以看成是有很多个小隔间组成的 彼此不干扰
在同一台计算机上的多个应用程序在内存中是相互隔离的(物理级别隔离)
如果真的想交互 需要借助于管道或者队列
- 代码验证
判断主进程中money的值是否改变,没有改变则说明父进程与子进程之间数据隔离
from multiprocessing import Process money = 100 def task(): global money money = 666 print('子进程打印的money', money) # 子进程打印的money 666 if __name__ == '__main__': p = Process(target=task) p.start() p.join() print('父进程打印的money', money) # 父进程打印的money 100进程间通信(IPC机制)
-
思考
1.什么是队列?
先进先出,后进后出
创建队列:q = Queue(3) (括号内指定队列可以容纳的数据个数) 默认:2147483647
队列添加数据:q.put(数据)
判断队列是否已经存满:q.full() (返回的是True,False)
队列中取数据:q.get() (没有值的时候,不会报错,会一直处于阻塞状态等待数据值的传入)
队列中取数据:q.get_nowait() (没有值的时候,会直接报错)
判断队列是否已经空了:q.empty()
注意:上述方法在多进程下不能准确使用(失效)!!!
-
IPC机制
1.主进程与子进程通信 2.子进程与子进程通信
-
代码实操
from multiprocessing import Process, Queue import time def task(q): print(f'{q.get()}你好呀') time.sleep(3) print(f'我{q.get()}很好') def run(q): q.put('lzq') q.put('gsy') if __name__ == '__main__': q = Queue(2) p = Process(target=run, args=(q,)) p1 = Process(target=task, args=(q,)) p.start() p1.start() p1.join() print('主进程') ''' 输出顺序: lzq你好呀 我gsy很好 主进程 '''
生产者 产生数据 消费者 处理数据
举例说明:
生产者:获取网页数据的代码(函数) 爬取 消费者:从网页数据中筛选出符合条件的数据(函数) 筛选
完整的生产者消费者模型至少有三个部分
生产者 消息队列/数据库 消费者进程相关方法
-
常见的属性和方法
pid:获取子进程id (ppid:获取父进程的id) name:获取子进程name属性 terminate:杀死子进程 is_alive:查看子进程是否还活着
-
PID的含义
在内存中开启多个进程,操作系统利用PID区分这些进程,每个进程都有一个唯一PID表示
-
PID获取方法
1.终端查看所有pid tasklist
2.指定具体的PID tasklist | findstr python
3.代码查看pid os 模块
4.current_process函数()
from multiprocessing import Process, current_process
current_process().pid
守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。
1.如何理解守护?
伴随着守护对象的存活而存活 死亡而死亡
2.代码实操
from multiprocessing import Process import time def task(name): print('大内总管:%s存活' % name) time.sleep(3) print('大内总管:%s嗝屁' % name) if __name__ == '__main__': p = Process(target=task, args=('基佬',)) p.daemon = True # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束 p.start() print('天子Jason寿终正寝!!!')
注意:p.daemon = True 必须在start之前执行
僵尸进程与孤儿进程- 进程运行了解
每个进程退出时,内核释放应该进程所有的资源,但是还会保留一部分的信息,如:进程号,进程状态,运行时间.直到主进程利用这个waitepid()方法,才能够完全释放,回收子进程的资源
僵尸进程 进程已经运行结束 但是相关的资源并没有完全清空 需要父进程参与回收 孤儿进程 父进程意外死亡 子进程正常运行 该子进程就称之为孤儿进程 孤儿进程也不是没有人管 操作系统会自动分配福利院接收
僵尸进程的劣势
僵尸进程的资源无法被释放,长期占用.内存压力增大.当新的程序来处理时,则会因为资源不足而导致运行失败.
互斥锁- 模拟抢票
from multiprocessing import Process import time import json import random # 查票 def search(name): with open(r'data.json', 'r', encoding='utf8') as f: data = json.load(f) print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num'))) # 买票 def buy(name): # 再次确认票 with open(r'data.json', 'r', encoding='utf8') as f: data = json.load(f) # 模拟网络延迟 time.sleep(random.randint(1, 3)) # 判断是否有票 有就买 if data.get('ticket_num') > 0: data['ticket_num'] -= 1 with open(r'data.json', 'w', encoding='utf8') as f: json.dump(data, f) print('%s买票成功' % name) else: print('%s很倒霉 没有抢到票' % name) def run(name): search(name) buy(name) if __name__ == '__main__': for i in range(10): p = Process(target=run, args=('用户%s'%i, )) p.start()
但是这样出现有很大的问题
今日作业1.尝试将TCP服务端制作成并发效果
客户端服务端全部设置成自动发消息自动回消息
eg: 客户端发hello 服务端直接转大写回HELLO
服务端:
import socket from multiprocessing import Process def run(): server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) return server def server1(sort): data = sort.recv(1024) sort.send(('你的信息:%s' % data.upper()).encode('utf8')) print(data.decode('utf8')) if __name__ == '__main__': server = run() number = 0 while number < 6: sort, address = server.accept() # 循环等待客人的到来 p = Process(target=server1, args=(sort,)) p.start() number += 1
客户端
import socket while True: client = socket.socket() client.connect(('127.0.0.1', 8080)) client.send('hello'.encode('utf8')) print(client.recv(1024).decode('utf8'))