网络编程周回顾

软件开发架构

规定了程序的请求逻辑、功能分块

1.C/S架构
	Client:客户端
 	Server:服务端
"""
我们使用计算机下载下来的一个个app本质是各大互联网公司的客户端软件
通过这些客户端软件我们就可以体验到各个互联网公司给我们提供的服务
eg:
下载淘宝客户端 打开 体验淘宝服务端提供的购物服务
下载抖音客户端 打开 体验抖音服务端提供的视频服务 
ps:一般情况下客户端与服务端交互需要互联网 但是有些不需要(因为客户端和服务端都在一台计算机上)

客户端:即将要去消费的客人
服务端:给客人提供服务的店

作为服务端必备的多个条件
1.24小时不间断提供服务
2.固定的地址
3.能够服务多个客人(高并发)
"""
2.B/S架构
	Browser:浏览器
	Server:服务器/端
	"""
	浏览器可以充当所有服务端的客户端

	ps:B/S架构本质还是C/S架构
	"""

'''
C/S架构
优势:不同公司的客户端由不同公司独立开发 可以高度定制化客户端功能
劣势:需要下载才能使用
B/S架构
优势:不用下载直接访问
劣势:无法高度定制化 并且需要遵守很多规则
'''

架构总结

ATM:三层架构
选课系统:三层架构
本质也属于软件开发架构的范畴

软件设计的大方向>>>:统一接口
	微信小程序
	支付宝小程序

网络编程前戏

1.什么是网络编程
	基于网络编写代码 能够实现数据的远程交互
2.学习网络编程的目的
	能够开发cs架构的软件
3.网络编程的起源
	"""
	最早起源于美国军事领域
		想实现计算机之间数据的交互
			最早的时候只能用硬盘拷贝
			之后发明了网络编程
	"""
4.网络编程必备条件
	数据的远程交互
 		1.早期的电话
        	电话线
  		2.早期的大屁股电脑
        	网线
 		3.笔记本电脑、移动电话
        	网卡
	ps:实现数据的远程交互必备的基础条件是物理连接介质

OSI七层协议简介

"""
OSI七层协议:规定了所有的计算机在远程数据交互的时候必须经过相同的处理流程、在制造过程中必须拥有相同的功能硬件
"""
应用层
表示层
会话层
传输层
网络层
数据链路层
物理连接层
ps:应、表、会、传、网、数、物

'''常见的是整合之后五层或者四层'''
应用层
传输层
网络层
数据链路层
物理连接层

应用层
传输层
网络层
网络接口层

"""
接收网络消息 数据由下往上传递
发送网络消息 数据由上往下传递
"""

OSI七层协议之物理连接层

主要用于确保计算机之间的物理连接介质 接收数据(bytes类型、二进制)

OSI七层协议之数据链路层

1.规定了电信号的分组方式
2.以太网协议
	规定了计算机在出厂的时候都必须有一块网卡 网卡上有一串数字
	该数字相当于是计算机的身份证号码是独一无二的
	该数字的特征:12位16进制数据
        	前6位产商编号 后6位流水线号
 	该数字也称为:以太网地址/MAC地址

网络相关专业名词

计算机之间要想实现数据交互必须要'连接'到一起

1.交换机
	能够将所有接入交换机的计算机彼此互联起来
2.广播
	首次查找接入同一个交换机的其他计算机 需要朝交换机里面吼一嗓子
3.单播
	首次被查找的计算机回应查找它的计算机 并附带自己的mac地址
4.广播风暴
	接入同一台交换机的多台计算机同时发广播
5.局域网
	可以简单的理解为有单个交换机组成的网络
	在局域网内可以直接使用mac地址通信
6.广域网
	可以简单的理解为范围更大的局域网
7.互联网
	由所有的局域网、广域网连接到一起形成的网络
8.路由器
	不同的局域网计算机之间是无法直接实现数据交互的 需要路由器连接

OSI七层协议之网络层

IP协议:规定了所有接入互联网的计算机都必须有一个IP地址 类似于身份证号
	mac地址是物理地址可以看成永远无法修改
	IP地址是动态分配的 不同的场所IP是不同的
IP地址特征:
    IPV4:点分十进制
    	0.0.0.0
      255.255.255.255
    IPV6:能够给地球上每一粒沙分一个IP地址
	 IP地址可以跨局域网传输
ps:IP地址可以用来标识全世界独一无二的一台计算机

OSI七层协议之传输层

PORT协议(端口协议)
	用来标识一台计算机上面的某一个应用程序
	范围:0-65535
 	特征:动态分配(洗浴中心号码牌)
	建议: 
       0-1024  		 系统默认需要使用
    	1024-8000 	  常见软件的端口号  
       8000之后的   

URL:统一资源定位符(网址)
    网址本质是有IP和PORT组成的!!!

IP+PORT:能够定位全世界独一无二的一台计算机上面的某一个应用程序

域名解析:将网址解析成IP+PORT

我们之所以不直接使用IP+PORT的原因是太难记 所以发明了域名(网址)

IP:PORT  实际使用冒号连接
    114.55.205.139:80

传输层之TCP与UDP协议

TCP与UDP都是用来规定通信方式的
   通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊
   随心所欲的聊:文字 图片 视频
   遵循一些协议:开头带尊称 首行空两格 只准用官话

 ps:不遵循上诉协议也可以通信 只不过遵循了更合规合法合理
1.TCP协议(重要)
  三次握手建链接
  1.TCP协议也称为可靠协议(数据不容易丢失)
    造成数据不容易丢失的原因不是因为有双向通道 而是因为有反馈机制
    给对方发消息之后会保留一个副本 直到对方回应消息收到了才会删除
    否则会在一定的时间内反复发送
  2.洪水攻击
    同一时间有大量的客户端请求建立链接 会导致服务端一致处于SYN_RCVD状态

  3.服务端如何区分客户端建立链接的请求
    可以对请求做唯一标识
四次挥手断链接
 1.四次不能合并为三次
    因为中间需要确认消息是否发完(TIME_WAIT)
 """
 三次握手和四次挥手也看看而已看成是小情侣谈恋爱的过程
          三次握手:表白在一起
          四次挥手:决裂要分手
 """
2.UDP协议
  也称之为数据协议、不可靠协议
 早期的QQ使用的是纯生的(没有加任何额外功能)UDP协议
 现在的QQ自己添加了很多技术和功能
    使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了
    """
    TCP协议类似于打电话:你一句我一句
    UDP我们可以看成是发短信:只要发了就行 不管对方看不看
"""

应用层

应用层相当于是程序员自己写的应用程序 里面的协议非常的多
常见的有:HTTP、HTTPS、FTP

socket模块

如果我们需要编写基于网络进行数据交互的程序 意味着我们需要自己通过代码来控制我们之前所学习的OSI七层(很繁琐 很复杂 类似于我们自己编写操作系统)
socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口

socket也叫套接字
     基于文件类型的套接字家族(单机)
    AF_UNIX
     基于网络类型的套接字家族(联网)
        AF_INET

socket基本使用

客户端
import socket

# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('192.168.1.118', 8080))
# 3.直接给服务端发送消息
client.send('不知道'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()
服务端
import socket


"""
以后要养成查看源码编写代码的思路
"""
# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()  # 括号内不写参数 默认就是TCP协议  family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('192.168.1.118', 8080))  # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池(暂且忽略)
server.listen(5)
# 4.等待接客
sock, addr = server.accept()  # return sock, addr  三次握手
print(sock, addr)  # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024)  # 接收客户端发送过来的消息 1024字节
print(data.decode('utf8'))
sock.send('晚饭吃什么'.encode('utf8'))  # 给客户端发送消息 注意消息必须是bytes类型
# 6.关闭双向通道
sock.close()  # 四次挥手
# 7.关闭服务端
server.close()  # 店倒闭了

代码优化

1.聊天内容自定义
	针对消息采用input获取
2.让聊天循环起来
	将聊天的部分用循环包起来
3.用户输入的消息不能为空
	本质其实是两边不能都是recv或者send 一定是一方收一方发
4.服务端多次重启可能会报错
	Address already in use 主要是mac电脑会报
  	方式1:改端口号
  	方式2:博客里面代码拷贝即可
5.当客户端异常断开的情况下 如何让服务端继续服务其他客人
	windows服务端会直接报错
  mac服务端会有一段时间反复接收空消息延迟报错
  	异常处理、空消息判断
服务端
import socket
server = socket.socket()
server.bind(('192.168.1.118', 8181))
server.listen(5)
while True: #链接循环
    sock, addr = server.accept()  # return sock, addr  三次握手

    while True: # 通信循环
        try:
            data = sock.recv(1024)  # 接收客户端发送过来的消息 1024字节
            if len(data) == 0:
                break
            print(f'来自客户端{addr}的消息>>>:',data.decode('utf8'))
            msg = input('请输入发送给客户端的消息(不能发空消息)>>>:').strip()
            sock.send(msg.encode('utf8'))
        except BaseException:
            break
客户端
import socket
client = socket.socket()
client.connect(('192.168.1.118', 8181))
while True:
    msg = input('请输入你想要发送给服务端的消息>>>:').strip()
    if len(msg) == 0:
        print('不能发送空消息')
        continue
    client.send(msg.encode('utf8'))
    data = client.recv(1024)
    print('来自于服务端发过来的消息>>>:',data.decode('utf8'))

半连接池的概念

server.listen(5)  # 半连接池

当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发问题)
假设服务端只有一个人的情况下

在测试半连接池的时候 可以不用input获取消息 直接把消息写死即可 

黏包现象

什么是粘包
1.服务端连续执行三次recv(字节数需要大一些)
import socket


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

sock, addr = server.accept()
data1 = sock.recv(1024)
print(data1)
data2 = sock.recv(1024)
print(data2)
data3 = sock.recv(1024)
print(data3)

sock.close()
server.close()
2.客户端连续执行三次send
import socket


client = socket.socket()
client.connect(('127.0.0.1', 8081))

client.send(b'hello kevin')
client.send(b'jason say hei')
client.send(b'jerry say goodbye')
问题:服务端一次性接收到了客户端三次的消息 该现象称为"黏包现象"
黏包现象产生的原因
1.不知道每次的数据到底多大
2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)

在知道了产生的原因后我们可以想到避免黏包现象的核心思路\关键点在于如何明确即将接收的数据具体有多大

ps:如何将长度变化的数据全部制作成固定长度的数据

struct模块及解决黏包问题的流程

struct模块
在处理黏包现象之前需要学习一个新模块:struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes(当然了,有长度限制)
import struct

info = b'hello big baby'
print(len(info))  # 数据真实的长度(bytes)  14
res = struct.pack('i', len(info))  # 将数据打包成固定的长度 i是固定的打包模式
print(len(res))  # 打包之后长度为(bytes)   4           报头

real_len = struct.unpack('i', res)
print(real_len)  # (14,)             根据固定长度的报头 解析出真实数据的长度


desc = b'hello my baby I will take you to play big ball'
print(len(desc))  # 数据真实的长度(bytes)  46
res1 = struct.pack('i', len(desc))
print(len(res1))  # 打包之后长度为(bytes)  4           报头

real_len1 = struct.unpack('i', res1)
print(real_len1)  # (46,)              根据固定长度的报头 解析出真实数据的长度
这里我们发现struct模块可以把对应的数据类型打包成固定的二进制长度然后还可以解压回去。
解决黏包问题初级版本
客户端
1.将真实数据转成bytes类型并计算长度
2.利用struct模块将真实长度制作一个固定长度的报头
3.将固定长度的报头先发送给服务端 服务端只需要在recv括号内填写固定长度的报头数字即可
4.然后再发送真实数据

服务端
1.服务端先接收固定长度的报头
2.利用struct模块反向解析出真实数据长度
3.recv接收真实数据长度即可

解决过程中遇到的问题
问题1:

struct模块无法打包数据量较大的数据,就算换更大的模式也不行

问题2:

报头能否传递更多的信息 比如电影大小 电影名称 电影评价 电影简介
解决黏包问题终极解决方案
字典作为报头打包 效果更好 数字更小

import struct

data_dict = {
    'file_name': 'xxx老师教学.avi',
    'file_size': 123132131232342342423423423423432423432,
    'file_info': '内容很精彩 千万不要错过',
    'file_desc': '一代神作 私人珍藏'
}
import json
data_json = json.dumps(data_dict)
print(len(data_json.encode('utf8')))  # 真实字典的长度  228
res = struct.pack('i', len(data_json.encode('utf8')))
print(len(res))
客户端
1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
2.利用struct模块制作字典的报头
3.发送固定长度的报头(解析出来是字典的长度)
4.发送字典数据
5.发送真实数据
服务端
1.接收固定长度的字典报头
2.解析出字典的长度并接收
3.通过字典获取到真实数据的各项信息
4.接收真实数据长度

粘包代码实战

服务端
import socket
import struct
import json


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

sock, addr = server.accept()
# 1.接收固定长度的字典报头
data_dict_head = sock.recv(4)
# 2.根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i', data_dict_head)[0]
# 3.接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)  # 自动解码再反序列化
# 4.获取真实数据的各项信息
# total_size = data_dict.get('file_size')
# with open(data_dict.get('file_name'), 'wb') as f:
#     f.write(sock.recv(total_size))
'''接收真实数据的时候 如果数据量非常大 recv括号内直接填写该数据量 不太合适 我们可以每次接收一点点 反正知道总长度'''
# total_size = data_dict.get('file_size')
# recv_size = 0
# with open(data_dict.get('file_name'), 'wb') as f:
#     while recv_size < total_size:
#         data = sock.recv(1024)
#         f.write(data)
#         recv_size += len(data)
#         print(recv_size)
客户端
import socket
import os
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1', 8081))

'''任何文件都是下列思路 图片 视频 文本 ...'''
# 1.获取真实数据大小
file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt')
# 2.制作真实数据的字典数据
data_dict = {
    'file_name': '有你好看.txt',
    'file_size': file_size,
    'file_desc': '内容很长 准备好吃喝 我觉得营养快线挺好喝',
    'file_info': '这是我的私人珍藏'
}
# 3.制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
# 4.发送字典报头
client.send(data_dict_len)  # 报头本身也是bytes类型 我们在看的时候用len长度是4
# 5.发送字典
client.send(data_dict_bytes)
# 6.最后发送真实数据
with open(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt', 'rb') as f:
    for line in f:  # 一行行发送 和直接一起发效果一样 因为TCP流式协议的特性
        client.send(line)
import time
time.sleep(10)

UDP协议(了解)

1.UDP服务端和客户端'各自玩各自的'
2.UDP不会出现多个消息发送合并

并发编程理论

研究网络编程其实就是在研究计算机的底层原理及发展史

"""
计算机中真正干活的是CPU 
"""
操作系统发展史
	1.穿孔卡片阶段
  	计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多时候计算机都不工作
    	好处:程序员独占计算机 为所欲为
  		坏处:计算机利用率太低 浪费资源
  2.联机批处理系统
  	提前使用磁带一次性录入多个程序员编写的程序 然后交给计算机执行
    	CPU工作效率有所提升 不用反复等待程序录入
	3.脱机批处理系统
  	极大地提升了CPU的利用率
	总结:CPU提升利用率的过程

多道技术

"""
在学习并发编程的过程中 不做刻意提醒的情况下 默认一台计算机就一个CPU(只有一个干活的人)
"""
单道技术
	所有的程序排队执行 过程中不能重合
多道技术
	利用空闲时间提前准备其他数据 最大化提升CPU利用率

多道技术详细
	1.切换
  	计算机的CPU在两种情况下会切换(不让你用 给别人用)
    	1.程序有IO操作
      	输入\输出操作
        	input、time.sleep、read、write
  		2.程序长时间占用CPU
      	我们得雨露均沾 让多个程序都能被CPU运行一下

  2.保存状态
  	CPU每次切换走之前都需要保存当前操作的状态 下次切换回来基于上次的进度继续执行
 
"""
开了一家饭店 只有一个服务员 但是同时来了五桌客人
	请问:如何让五桌客人都感觉到服务员在服务他们
		让服务员化身为闪电侠 只要客人有停顿 就立刻切换到其他桌 如此往复
"""

进程理论

进程与程序的区别
	程序:一堆死代码(还没有被运行起来)
	进程:正在运行的程序(被运行起来了)

进程的调度算法(重要)
	1.FCFS(先来先服务)
  	对短作业不友好
	2.短作业优先调度
  	对长作业不友好
	3.时间片轮转法+多级反馈队列(目前还在用)
  	将时间均分 然后根据进程时间长短再分多个等级
    等级越靠下表示耗时越长 每次分到的时间越多 但是优先级越低

进程的并行与并发

并行
	多个进程同时执行 必须要有多个CPU参与 单个CPU无法实现并行
并发
	多个进程看上去像同时执行 单个CPU可以实现 多个CPU肯定也可以

判断下列两句话孰对孰错
	我写的程序很牛逼,运行起来之后可以实现14个亿的并行量
  	并行量必须要有对等的CPU才可以实现
  我写的程序很牛逼,运行起来之后可以实现14个亿的并发量
  	合情合理 完全可以实现	以后我们的项目一般都会追求高并发
ps:目前国内可以说是最牛逼的>>>:12306

进程的三状态

就绪态
所有的进程在被CPU执行之前都必须先进入就绪态等待
运行态
CPU正在执行
阻塞态
进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态

同步与异步

用来表达任务的提交方式

同步
	提交完任务之后原地等待任务的返回结果 期间不做任何事
异步
	提交完任务之后不愿地等待任务的返回结果 直接去做其他事 有结果自动通知

阻塞与非阻塞

用来表达任务的执行状态

阻塞
	阻塞态
非阻塞
	就绪态、运行态

综合使用

同步阻塞
同步非阻塞
异步阻塞
异步非阻塞(******)
	效率最高

创建进程的多种方式

"""
1.鼠标双击软件图标
2.python代码创建进程
"""
"""
在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""
from multiprocessing import Process
import time


def task():
    print('task is running')
    time.sleep(3)
    print('task is over')

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    # task()
    print('主')
from multiprocessing import Process
import time


class MyProcess(Process):
    def run(self):
        print('run is running')
        time.sleep(3)
        print('run is over')
if __name__ == '__main__':
    obj = MyProcess()
    obj.start()
    print('主')
# 创建进程的两种方式的传参
from multiprocessing import Process
import time


def task(name):
    print('task is running',name)
    time.sleep(3)
    print('task is over',name)

if __name__ == '__main__':
    p1 = Process(target=task, args=('jason',))  # 位置参数
    p1 = Process(target=task, kwargs={'name':'jason123'})  # 关键字参数
    p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
    # task()  # 同步
    print('主')
from multiprocessing import Process
import time
class MyProcess(Process):
    def __init__(self, name, age):
        super.__init__()
        self.name = name
        self.age = age

    def run(self):
        print('run is running', self.name, self.age)
        time.sleep(3)
        print('run is over', self.name, self.age)
if __name__ == '__main__':
    obj = MyProcess('jason', 123)
    obj.start()
    print('主')

进程间数据隔离

同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)

from multiprocessing import Process
import time

money = 1000


def task():
    global money
    money = 666
    print('子进程的task函数查看money', money)


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()  # 创建子进程
    time.sleep(3)  # 主进程代码等待3秒
    print(money)  # 主进程代码打印money

进程的join方法

from multiprocessing import Process
import time

def task(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)

if __name__ == '__main__':
    p1 = Process(target=task, args=('jason1', 1))
    p2 = Process(target=task, args=('jason2', 2))
    p3 = Process(target=task, args=('jason3', 3))
    # p.start()  # 异步
    '''主进程代码等待子进程代码运行结束再执行'''
    # p.join()
    # print('主')
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    print(time.time() - start_time)  # 6秒多


    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print(time.time() - start_time) # 3秒多

IPC机制

IPC:进程间通信
消息队列:存储数据的地方 所有人都可以存 也都可以取

from multiprocessing import Queue


q = Queue(3)  # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
# print(q.full())  # 判断队列是否已满
q.put(222)
q.put(333)
# print(q.full())  # 判断队列是否已满
# 从消息队列中取出数据
print(q.get())
print(q.get())
# print(q.empty())  # 判断队列是否为空
print(q.get())
# print(q.empty())  # 判断队列是否为空
# print(q.get())
print(q.get_nowait())

"""
full() empty() 在多进程中都不能使用!!!
"""
from multiprocessing import Process, Queue


def product(q):
    q.put('子进程p添加的数据')

def consumer(q):
    print('子进程获取队列中的数据', q.get())


if __name__ == '__main__':
    q = Queue()
    # 主进程往队列中添加数据
    # q.put('我是主进程添加的数据')
    p1 = Process(target=consumer, args=(q,))
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    print('主')

生产者消费者模型

"""回想爬虫"""
生产者
	负责产生数据的'人'
消费者
	负责处理数据的'人'

该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)

进程对象的多种方法

1.如何查看进程号
	from multiprocessing import Process, current_process
 	current_process()
 	current_process().pid  
	import os
 	os.getpid()
  	os.getppid()
2.终止进程
	p1.terminate()
	ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
	p1.is_alive()
4.start()
5.join()

守护进程

守护进程会随着守护的进程结束而立刻结束
	eg: jason是kevin的守护进程 一旦kevin嗝屁了 jason立刻嗝屁

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=('kevin',))
    p1.daemon = True
    p1.start()
    time.sleep(1)
    print('恕瑞玛皇帝:jason去世了')

僵尸进程与孤儿进程

僵尸进程
进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来
 比如进程号、进程执行时间、进程消耗功率等给父进程查看
 ps:所有的进程都会变成僵尸进程
孤儿进程
子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理

多进程数据错乱问题

模拟抢票软件

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()

"""
多进程操作数据很可能会造成数据错乱>>>:互斥锁
	互斥锁
		将并发变成串行 牺牲了效率但是保障了数据的安全
"""
posted @ 2022-11-27 18:09  wwwxxx123  阅读(49)  评论(0编辑  收藏  举报