day35(lock)
今日内容详细加昨日内容回顾
UDP协议:
socket.socket(type=socket.SOCK_DGRAM) # 自己指定UDP协议(默认是TCP协议)
UDP TCP
recvfrom() recv()
sendto() send()
操作系统的发展史
从1.穿孔卡片
2.联机批处理
到3.脱机批处理
# 总结:操作系统的发展史其实就是提升CPU利用率的过程
多道技术
# 目的:提升CPU利用率 降低程序等待时间
'''强调:目前我们研究并发都是以计算机是单核的情况下:只有一个CPU'''
串行
多个任务排队执行 总耗时就是多个任务完整时间叠加
多道
利用空闲提前准备 缩短总的执行时间并且还能提高CPU利用率
1.空间上的复用
多个任务共用一套计算机硬件
2.时间上的复用
切换+保存状态
CPU在两种情况下会被拿走
进程理论
进程理论与程序的区别
程序:一堆没有被执行的代码(死的)
进程:正在运行的程序(活的)
#为了更加精确的描述出一些实际状态就有了进程这个概念
# 2.进度调度算法
先来先服务
短作业优先
时间片轮转法:先公平的将CPU分给每个人执行
多级反馈队列:根据作业长短的不同再合理分配CPU执行时间
'''目的就是为了能够让单核的计算机也能够做到运行多个程序'''
#并发与并行
首先说一下今天了解到的一个新名词 """星轨 星轨的意思就是明星出轨, 微博以前只要有明星出轨就会立刻蹦掉 后续一直在优化 现在微博号称可以同时架得住八个星轨(八个明星出轨)都不会蹦当然是那种比较大的有名气的明星
在行业中星轨有时候也会用来描写描述该软件的一个并发量
"""
并发:看上去像同时在执行就可以称之为是并发(单个CPU就可以)
并行:必须同一时间同时执行("""单核计算机肯定不能实现并行!!! 必须要有多个CPU""")
ps:以后我们写软件都会用一个名词来表示自己写的软件有多牛逼
并发量: 1千万 2千万 3千万 (越多越牛逼)
# 2.同步与异步
'''描述的是任务的提交方式'''
同步:提交完任务之后原地等待任务的结果 期间不做任何事
异步:提交完任务之后不原地等待结果 结果通过反馈机制获取,也就是有结果自动提醒
# 3.阻塞与非阻塞
'''描述的是任务的执行状态'''
进程三状态
阻塞:阻塞态
非阻塞:就绪态 运行态
'''
如果想要尽可能的提升程序执行效率
就要想办法让我们的程序一直处于就绪态和运行态(不要有IO操作)
'''
今日内容详细
"""创建进程的方式:我们其实很早就接触过了比方说你双击点开一个微信就是创建一个进程(鼠标双击桌面一个应用图标)
代码创建进程
"""
(某提普肉赛行)
在python中提供了一个multiprocessing模块可以帮助我们使用多进程解决问题。
再multiprocessing模块中有一个类Process,它是我们这次的主角,
"""不同的操作系统创建进程的要求不一样
在winddows中创建十一导入模块的方式进行的
而且创建的进程代码也必须写在__main__子代码之中
否则会报错因为这样会无限地创造进程
在linux和mac中创建进程是直接拷贝一份源代码然后执行 不需要写在__main__子代码中"""
2.代码创建
#导入模块
from multiprocessing import Process
import time
def task(name):
print('%s is running' % name)
time.sleep(3)
print('%s is over' % name)
if __name__ == '__main__':
p = Process(target=task, args=('jason',)) # 创建一个进程对象
p.start() # 告诉操作系统创建一个新的进程
print('主进程')
# 创建进程的第二种方式
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self, username):
self.username = username
super().__init__()
def run(self):
print('你好啊 小姐姐',self.username)
time.sleep(3)
print('get out!!!',self.username)
if __name__ == '__main__':
p = MyProcess('tony')
p.start()
print('主进程')
进程实现并发
join方法
join方法 主线程等待子线程终止。
1.join方法的简单使用
from multiprocessing import Process
import time
def task(name, n):
print(f'{name} is running')
time.sleep(n)
print(f'{name} is over')
if __name__ == '__main__':
p1 = Process(target=task, args=('jason', 1))
p2 = Process(target=task, args=('tony', 2))
p3 = Process(target=task, args=('kevin', 3))
start_time = time.time()
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
end_time = time.time() - start_time
print('主进程', f'总耗时:{end_time}')
# 主进程 总耗时:3.015652894973755
join方法:
import time
from multiprocessing import Process
def func_one(name):
print("My name is", name)
time.sleep(2)
print("This is func_one")
def func_two(name):
print("My name is", name)
time.sleep(2)
print("This is func_two")
if __name__ == '__main__':
p_one = Process(target=func_one, args=("Jack",))
p_two = Process(target=func_two, args=("Mick",))
p_one.start()
p_one.join() # 主线程要等待func_one终止,才继续往下走
p_two.start()
打印结果:
My name is Jack
This is func_one # 打印这一步之前等待了2秒
My name is Mick
This is func_two
进程间数据默认隔离
# 内存是可以看成是有很多个小隔间组成的 彼此是互不干扰
from multiprocessing import process
money = 99
def task():
global money # 我们以前学过的关键字glodal 局部修改全局不可变类型
money = 888
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join() # 主进程等待子进程结束 在打印 money
print(money)
# 打印结果 99 证实了 进程间数据默认隔离 彼此不干扰
、
"""默认隔离 但是可以通过一些技术打破"""
#内存可以看成是有很多个小隔间组成的 彼此不干扰
from multiprocessing import Process
def func_one():
global n
n = 0
print("在func_one中的n为%s" % n)
def func_two():
global n
n = 1
print("在func_two中的n为%s" % n)
if __name__ == '__main__':
n = 100
p_one = Process(target=func_one)
p_two = Process(target=func_two)
p_one.start()
p_two.start()
print("主进程的n为%s" % n)
打印结果
主进程的n为100
在func_one中的n为0
在func_two中的n为1
进程对象属性和方法
"""
进程号如何查看
windows: tasklist结果集中PID
mac: ps -ef
"""
1.查看进程号的方法
1.1.current_process函数
from multiprocessing import Process, current_process
current_process().pid
Process属性方法介绍
方法/属性 | 说明 |
---|---|
start() | 启动进程,调用进程中的run()方法。 |
run() | 进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 。 |
terminaet() | 强制终止进程,不会进行任何清理操作。如果该进程终止前,创建了子进程,那么该子进程在其强制结束后变为僵尸进程;如果该进程还保存了一个锁那么也将不会被释放,进而导致死锁。使用时,要注意。 |
is_alive() | 判断某进程是否存活,存活返回True,否则False。 |
join([timeeout]) | 主线程等待子线程终止。timeout为可选择超时时间;需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 。 |
daemon | 默认值为False,如果设置为True,代表该进程为后台守护进程;当该进程的父进程终止时,该进程也随之终止;并且设置为True后,该进程不能创建子进程,设置该属性必须在start()之前 |
name | 进程名称。 |
pid | 进程pid |
exitcode | 进程运行时为None,如果为-N,表示被信号N结束了。 |
方法 | 说明 |
---|---|
authke | 进程身份验证,默认是由os.urandom()随机生成32字符的字符串。这个键的用途是设计涉及网络连接的底层进程间的通信提供安全性,这类连接只有在具有相同身份验证才能成功。 |
current_process().pid | .查看进程号 |
os.getpid() | 获取当前进程的进程号 |
os.getppid() | 获取当前进程的父进程号 |
![]() |
语法: | Process([group [, target [, name [, args [, kwargs]]]]]) |
---|---|
参数介绍: | 参数说明 |
group: | 参数未使用,默认值为None。 |
target: | 表示调用对象,即子进程要执行的任务。 |
args: | 表示调用的位置参数元祖。 |
kwargs: | 表示调用对象的字典。如kwargs = {'name':Jack, 'age':18}。 |
name: | 子进程名称。 |
僵尸进程与孤儿进程
僵尸进程
# 为什么主进程默认需要等待子进程结束才会结束
所有的子进程在运行结束之后都会变成僵尸进程(死了没死透)
还保留着pid和一些运行过程的中的记录便于主进程查看(短时间保存)
这些信息会被主进程回收(僵尸彻底死了)
1.主进程正常结束
2.调用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=('赵公公',))
# 必须写在start前面
p.daemon = True # 将子进程设置为守护进程:主进程结束 子进程立刻结束默认值为False,如果设置为True,代表该进程为后台守护进程;当该进程的父进程终止时,该进程也随之终止;并且设置为True后,该进程不能创建子进程,设置该属性必须在start()之前
p.start()
print('皇帝Jason寿终正寝')
互斥锁(重要)
通过上面的研究,我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题。
当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题:
import json
from multiprocessing import Process
import time
import random
# 查票
def search(name):
with open(r'ticket_data.json', 'r', encoding='utf8') as f:
data = json.load(f)
print(f'{name}查询当前余票:%s' % data.get('ticket_num'))
# 买票
def buy(name):
'''
点击买票是需要再次查票的 因为期间其他人可能已经把票买走了
'''
# 1.查票
with open(r'ticket_data.json', 'r', encoding='utf8') as f:
data = json.load(f)
time.sleep(random.randint(1, 3))
# 2.判断是否还有余票
if data.get('ticket_num') > 0:
data['ticket_num'] -= 1
with open(r'ticket_data.json', 'w', encoding='utf8') as f:
json.dump(data, f)
print(f'{name}抢票成功')
else:
print(f'{name}抢票失败 没有余票了')
def run(name):
search(name)
buy(name)
# 模拟多人同时抢票
if __name__ == '__main__':
for i in range(1, 10):
p = Process(target=run, args=('用户:%s' % i,))
p.start()
##########################################
当多个进程操作同一份数据的时候会造成数据的错乱!!!
这个时候需要加锁处理(互斥锁)
将并发变成串行 牺牲了效率但是保证的数据的安全
互斥锁并不能轻易使用 容易造成死锁现象
互斥锁只在处理数据的部分加锁 不能什么地方都加 严重影响程序的效率
##########################################
查票可以一次性给所有人看 但是买票环节必须'排队'>>>:互斥锁
from multiprocessing import Process, Lock
mutex = Lock()
mutex.acquire() # 抢锁
mutex.release() # 放锁
红绿灯实例
from multiprocessing import Process, Event
import time
import random
def traffic_light(e):
'''信号灯函数'''
while 1: # 红绿灯得一直亮着,要么是红灯要么是绿灯
if e.is_set(): # True,代表绿灯亮,那么此时代表可以过车
time.sleep(5) # 所以在这让灯等5秒钟,这段时间让车过
print('\033[31m红灯亮!\n车辆等待中...\033[0m') # 绿灯亮了5秒后应该提示到红灯亮
e.clear() # 把is_set设置为False
else:
time.sleep(5) # 此时代表红灯亮了,此时应该红灯亮5秒,在此等5秒
print('\033[32m绿灯亮!\n车辆通过中...\033[0m') # 红的亮够5秒后,该绿灯亮了
e.set() # 将is_set设置为True
def car_status(num, e):
e.wait() # 车等在红绿灯,此时要看是红灯还是绿灯,如果is_set为True就是绿灯,此时可以过车
print('第%s辆车过去了' % num)
if __name__ == '__main__':
event = Event()
tra_light = Process(target=traffic_light, args=(event,)) # 信号灯的进程
tra_light.start()
for i in range(50): # 描述50辆车的进程
if i % 3 == 0:
time.sleep(random.randint(1, 5)) # 车辆出现时间随机
car = Process(target=car_status, args=(i + 1, event,))
car.start()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下