4.23博客
今日内容:
- 进程对象以及其他方法
- 僵尸进程与孤儿进程
- 守护进程
- 互斥锁
- 队列
- 进程间的通信IPC机制
- 生产消费者模型
- 线程
1、进程对象以及其他方法
计算机会给各个运行的进程分配进程进程控制符PID,也可以称之为进程号( Process Identifier),用于区分和管理各个进程
如果查看: windows电脑 进入cmd输入tasklist即可查看
tasklist |findstr PID 查看具体的进程;mac电脑 进入终端之后输入ps aux,ps aux|grep PID查看具体的进程 。
from multiprocessing import Process, current_process
import time
import os
# def task():
# # print('%s is running'%current_process().pid) # 查看当前进程的进程号
# print('%s is running'%os.getpid()) # 查看当前进程的进程号
# # print('子进程的主进程号%s'%os.getppid()) # 查看当前进程的进程号
# time.sleep(30)
def task():
# 查看当前进程的进程号
print(f'{current_process().pid} is running')
# 用os模块的方法查看进程号
print(f'{os.getpid()}')
# 用os模块的方法查看当前进程的父进程号
print(f'{os.getppid()}')
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
# 启动进程
p.start()
# 杀死当前进程
# ps: 只是告诉了操作系统去杀死当前进程,
# 但是这个操作需要一定的时间,然后代码的运行速度是极快的
# p.terminate()
# 因为代码运行速度极快,进程还没有真正的杀死掉,
# 打印当前进程存活状态为True
# print(p.is_alive()) # 判断进程是否存活
print('主')
ps:一般情况下我们会默认的将布尔值的变量名和返回布尔值的方法名以is_这种形式开头
2、僵尸进程与孤儿进程
-
僵尸进程:
通俗来讲就是程序死了但没有完全死透,没有死透意思是说进程死后进程所占的进程号不会立即释放。
原因:
是为了让父进程能够查看到自己子进程的信息,比如说:占用的PID号,开启的时间,运行时间,终止的时间...
会出现的问题:
所有的进程终止后多会步入僵尸进程,如果父进程不死并且无限制的造子进程,这个时候子进程又不结束,这种情况会严重的消耗计算机资源
僵尸进程PID回收办法:
-
父进程等待子进程运行结束
-
父进程调用join方法
import time def run(): print('hello world') time.sleep(1) print('get out') if __name__ == '__main__': p = Process(target=run) p.start() print('主') ''' 主 hello world get out'''
-
-
孤儿进程:
创建子进程的父进程意外死亡,而子进程还存活,那么这个子进程就是孤儿进程,操作系统会开始init进程所收养孤儿进程,由init进程对它们完成状态的收集工作。
3、守护进程
被守护进程结束,守护进程跟着结束
def task(name):
print(f'{name}正在上班')
time.sleep(2)
print(f'{name}下班了')
if __name__ == '__main__':
p=Process(target=task,args=('tom',))
p.start()
print('公司没了')
'''
公司没了
tom正在上班
tom下班了'''
def task(name):
print(f'{name}正在上班')
time.sleep(2)
print(f'{name}下班了')
if __name__ == '__main__':
p=Process(target=task,args=('tom',))
# 将进程p设置成守护进程
# 这一句一定要放在start方法上面才有效否则会直接报错
p.daemon=True
p.start()
print('公司没了')
'''
公司没了
'''
4、互斥锁
由来:如果多个进程都在操控同一份数据时,会出现数据错乱的问题,针对该问题的解决办法就是加锁处理:将并发改为串行,虽然牺牲掉了效率但是能够保证数据的安全。
from multiprocessing import Process, Lock
import json
import time
import random
#文件data.txt内容:
'''
{"ticket_num": 3}'''
# 查票
def search(i):
# 文件操作读取票数
with open('data','r',encoding='utf8') as f:
dic = json.load(f)
print('用户%s查询余票:%s'%(i, dic.get('ticket_num')))
# 字典取值不要用[]的形式 推荐使用get 你写的代码打死都不能报错!!!
# 买票 1.先查 2.再买
def buy(i):
# 先查票
with open('data','r',encoding='utf8') as f:
dic = json.load(f)
# 模拟网络延迟
time.sleep(random.randint(1,3))
# 判断当前是否有票
if dic.get('ticket_num') > 0:
# 修改数据库 买票
dic['ticket_num'] -= 1
# 写入数据库
with open('data','w',encoding='utf8') as f:
json.dump(dic,f)
print('用户%s买票成功'%i)
else:
print('用户%s买票失败'%i)
# 整合上面两个函数
def run(i, mutex):
search(i)
# 给买票环节加锁处理
# 抢锁
mutex.acquire()
buy(i)
# 释放锁
mutex.release()
if __name__ == '__main__':
# 在主进程中生成一把锁 让所有的子进程抢 谁先抢到谁先买票
mutex = Lock()
for i in range(1,11):
p = Process(target=run, args=(i, mutex))
p.start()
'''
{"ticket_num": 0}'''
注意:
1.锁不要轻易的使用,容易造成死锁现象(我们写代码一般不会用到,都是内部封装好的)
2.锁只在处理数据的部分加来保证数据安全(只在争抢数据的环节加锁处理即可)
5、队列
之前我们学过subpress管道,队列可以理解为管道加锁
队列的特点:先进来的先出去
堆栈:后进来的先出去
from multiprocessing import Queue
# 获的一个队列
q = Queue(3) # 括号内指定存放数据的个数,不指定有默认值
# 往队列里放数据
q.put('111')
q.put('222')
# print(q.full())
q.put('333')
# print(q.full()) # 判断队列是否存满数据
# 当队列数据放满的时候还有数据要存放到队列中去,
# 程序就会堵塞,直到有队列中有位置让出来,但是不会报错
# q.put('444')
# 从队列里取数据,先放进去的先取出
v1 = q.get()
v2 = q.get()
# print(q.empty())
v3 = q.get()
# print(q.empty()) # 判断队列是否为空
# 队列中已经为空没有数据可取时,再用get取就会阻塞,直到有数据存入
# v4=q.get()
# 用get_nowait取数据,没有数据直接报错
# v4=q.get_nowait()
# 没有数据等待指定时间后再报错
# v4=q.get(timeout=3)
try:
v4 = q.get(timeout=3)
print(v3)
except Exception as e:
print('队列中没有数据了!')
print(v1, v2, v3)
注意:full()、empty()、getnowait() 在多进程的情况下时不精确的,因为多个进程操作数据,队列的数据会不断的变化。
6、进程间的通信IPC机制
含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
思路:
1.主进程跟子进程借助于队列通信
2.子进程跟子进程借助于队列通信
def producer(q):
q.put('一个包子')
print('做了一个包子')
def consumer(q):
print(f'吃掉了{q.get()}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
'''
做了一个包子
吃掉了一个包子'''
7、生产者消费者模型
生产者:
创造/生产东西的
消费者:
处理/消耗东西的
媒介:
生产者产生的东西放到媒介中,消费者再从媒介中取。
生产者(做包子的) + 消息队列(蒸笼) + 消费者(吃包子的)
8、线程理论
三问
1.线程是什么
线程是真正的执行单位,是代码运行的过程,可以理解为运行程序时干活了,而进程代表的是资源单位,内存中开辟的空间,进程之所以能被运行时因为每一个进程中都自带一个线程,执行代码中所需要使用到的资源都找所在的进程索要。
比喻:
将操作系统比喻成一个大的工厂,那么进程就相当于工厂里面的车间
,而线程就是车间里面的流水线。
注意:不管是线程还是进程都是一种虚拟的单位,只是为了让我们能够更好的描述问题
为何要有线程
我们知道设线程需要申请内存空间,需要实现多个功能的时候如果开设进程的话,对内存的占用就比较大,而如果我们选择在一个进程中开设多个线程,由于各进程可以同享进程中的数据,这样对内存占用就变得很小了,而且各个功能间还可以进行数据的交互。
例子:
我们要开发一款文本编辑器,它有三个功能
1)获取用户输入的功能
2)实时展示到屏幕的功能
3)自动保存到硬盘的功能
针对上面这三个功能,开三个线程处理上面的三个功能更加的合理
3.线程如何使用:psss
ps:人工智能相关参考网站
https://www.xfyun.cn/?ch=bd05&b_scene_zt=1
http://ai.baidu.com/creation/main/demo
pycharm过期解决办法:
如果你的pycharm老是过期,你可以直接下载最新版本的pycharm,然后加入一个网站获取激活码即可
http://idea.medeming.com/jets/