首页 > 编程语言 >网络编程与并发周末总结

网络编程与并发周末总结

时间:2022-11-20 22:35:18浏览次数:37  
标签:__ name 周末 编程 print 并发 进程 import data

目录

网络编程

软件开发架构

规定了程序的请求逻辑、功能分块
1.C/S架构
	Client:客户端
    Server:服务端
        客户端:即将要去消费的客人
        服务端:给客人提供服务的店
        *作为服务端必备的多个条件
        1.24小时不间断提供服务
        2.固定的地址
        3.能够服务多个客人
2.B/S架构
	Browser:浏览器
    Server:服务器/服务端
    """浏览器可以充当所有服务端的客户端"""
"""
C/S架构:
	优势:不同公司的客户端由不同公司独立开发,可以高度定制化客户端功能
	缺点:需要下载才能使用
B/S架构:
	优势:不用下载直接访问
	缺点:无法高度定制化,并且需要遵守很多规定
"""

架构总结

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

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

网络编程理论

1.网络编程的起源
"""
最早起源于美国军事邻域,想实现计算机之间数据的交互,最早的时候只能用硬盘拷贝,所以之后发明了网络编程
"""
2.网络编程必备的三大条件:
    数据远程的交互
   电话线,网线,网卡
ps:实现数据远程的交互必备的基础条件是物理连接介质

OSI七层协议简介

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

应用层、传输层、网络层、网络接口层
"""
接收网络消息,数据由下往上传递
发送网络消息,数据由上往下传递
"""

OSI七层协议之数据链路层

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

网络层

IP协议:规定了所有接入互联网的计算机都必须有一个ip地址,类似于身份证号
mac地址是物理地址可以看成永远无法修改
IP地址是动态分配的,不同的场所IP是不同的

传输层

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都是用来规定通信方式的
	通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊
  	随性所欲的聊:文字 图片 视频 
  	遵循一些协议:开头带尊称 首行空两格 只准用官话 不能打情骂俏
1.TCP协议(重要)
三次握手建链接
	1.TCP协议也称为可靠协议(数据不容易丢失)
    造成数据不容易丢失的原因不是因为有双向通道,而是因为有反馈机制
    给对方发消息之后会保留一个副本,直到对方回应消息收到了才会删除
     否则会在一定的时间内反复发送
 		2.洪水攻击
    	同一时间有大量的客户端请求建立链接 会导致服务端一致处于SYN_RCVD状态
  	3.服务端如何区分客户端建立链接的请求
    	可以对请求做唯一标识
  四次挥手断链接
  	1.四次不能合并为三次
    	因为中间需要确认消息是否发完(TIME_WAIT)
2.UDP协议
	也称之为数据报协议、不可靠协议	
  早期的QQ使用的是纯生的(没有加任何额外功能)UDP协议
  现在的QQ自己添加了很多技术和功能
 	使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了

应用层

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

网络相关专业名词

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

socket模块

socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口

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

socket代码简介

import socket
# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()  # 括号内不写参数 默认就是TCP协议  family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('127.0.0.1', 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()  # 店倒闭了


import socket
# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送消息
client.send('大爷有钱 把你们店最好的给我叫出来'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()

半连接池的概念

server.listen(5)  # 半连接池

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

黏包现象

1.服务端连续执行三次recv
2.客户端连续执行三次send
问题:服务端一次性接收到了客户端三次的消息 该现象称为"黏包现象"
  
黏包现象产生的原因
	1.不知道每次的数据到底多大
	2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
 
避免黏包现象的核心思路\关键点
		如何明确即将接收的数据具体有多大
ps:如何将长度变化的数据全部制作成固定长度的数据

struct模块

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,)              根据固定长度的报头 解析出真实数据的长度

"""
解决黏包问题初次版本
    客户端
        1.将真实数据转成bytes类型并计算长度
        2.利用struct模块将真实长度制作一个固定长度的报头
        3.将固定长度的报头先发送给服务端 服务端只需要在recv括号内填写固定长度的报头数字即可
        4.然后再发送真实数据
    
    服务端
        1.服务端先接收固定长度的报头
        2.利用struct模块反向解析出真实数据长度
        3.recv接收真实数据长度即可
"""

'''问题1:struct模块无法打包数据量较大的数据 就算换更大的模式也不行'''
# res = struct.pack('i', 12313213123)
# print(res)
'''问题2:报头能否传递更多的信息  比如电影大小 电影名称 电影评价 电影简介'''

'''终极解决方案:字典作为报头打包 效果更好 数字更小'''
# 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利用率
      
多道技术详细
	1.切换
  	计算机的CPU在两种情况下会切换(不让你用 给别人用)
    	1.程序有IO操作
      	输入\输出操作
        	input、time.sleep、read、write
  		2.程序长时间占用CPU	
      	我们得雨露均沾 让多个程序都能被CPU运行一下 

  2.保存状态
  	CPU每次切换走之前都需要保存当前操作的状态 下次切换回来基于上次的进度继续执行

进程理论

进程与程序的区别
	程序:一堆死代码(还没有被运行起来)
	进程:正在运行的程序(被运行起来了)
 
进程的调度算法
	1.FCFS(先来先服务)
  	对短作业不友好
	2.短作业优先调度
  	对长作业不友好
	3.时间片轮转法+多级反馈队列(目前还在用)
  	将时间均分 然后根据进程时间长短再分多个等级
    等级越靠下表示耗时越长 每次分到的时间越多 但是优先级越低

进程的并行与并发

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

进程的三状态

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

同步与异步

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

阻塞与非阻塞

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

综合使用

同步阻塞
同步非阻塞
异步阻塞
异步非阻塞=效率最高  

创建进程的两种方式

1.鼠标双击软件图标

2.python代码创建进程

方式一:用函数创建

from multiprocessing import Process  # 多进程模块
import time


def task(name):
    print(f'{name}在运行')
    time.sleep(3)
    print(f'{name}运行结束')


if __name__ == '__main__':
    """同步操作"""
    # task('jason')
    # print('主进程')
    """
    运行结果:
       jason在运行
       jason运行结束
       主进程
    """
    
    """异步操作"""
    # 创建一个对象p
    p = Process(target=task,args=('jason',))  # 位置参数 (注意元组)
    #p = Process(target=task, kwargs={'name': 'jason'})  # 关键字参数 太麻烦一般不用
    # 用对象p去创建子进程去执行task函数
    p.start()
    print('主进程')
    """
    运行结果:
       主进程
       jason在运行
       jason运行结束
    """
      

"""
在不同操作系统中创建进程底层原理不同:
windows: 类似于导模块,要有__main__启动脚本
mac/linux:类似于直接拷贝,不需要__main__启动脚本,为了兼容最好加上
如果没有__main__会报错
"""

方式二:用面向对象创建

from multiprocessing import Process
import time

class MyProcess(Process):
    
    def run(self):
        print('run正在运行')
        time.sleep(3)
        print('run运行结束')
        
if __name__ == '__main__':
    obj=MyProcess()
    obj.start()
    print('主进程')
    """
    运行结果:
        主进程
        run正在运行
        run运行结束
    """

面向对象有参如何传:

from multiprocessing import Process
import time


class MyProcess(Process):
    def __init__(self, name):
        super().__init__() # 由于重写了__init__方法 但是本身的name默认是有一个子进程名字的,所以需调用super()方法 然后再修改里面的name (如果super()放在下一行那么name就会变成子进程的名字)
# 子进程的名字:MyProcess-1
        self.name = name

    def run(self):
        print(f'{self.name}在运行')
        time.sleep(3)
        print(f'{self.name}运行结束')

if __name__ == '__main__':
    obj = MyProcess('jason')
    obj.start()
    print('主进程')
    """
    运行结果:
       主进程
       jason在运行
       jason运行结束
    """

进程间数据间隔

同一台计算机中的多个进程数据彼此间默认是互相隔离的

from multiprocessing import Process
import time

money = 1000


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


if __name__ == '__main__':
    p = Process(target=task)
    p.start()  # 用对象p创建子进程去执行task函数
    time.sleep(3)  # 主进程代码等待3秒
    print('主进程money:', money)  # 主进程代码打印money
    """
    运行结果:
    子进程的task函数查看money 666
    主进程money: 1000
    """

因为同一台计算机中多个进程数据彼此间是互相隔离的,所以虽然用了global修改全局变量,但还是没有真正去修改主进程中的money

8)进程join方法

调用join方法会让主进程变为同步操作,子进程还是异步 (主进程等子进程运行结束后再运行)

from multiprocessing import Process
import time


def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{name}运行结束')


if __name__ == '__main__':
    """异步操作"""
    p = Process(target=task, args=('jason1', 1))
    p.start()
    # print('主进程')
    # 运行结果: 主进程 jason在运行 jason运行结束
    
    """加了join让主进程变成同步操作"""
    p.join()  # 加了join()方法后则执行完子进程再执行主进程
    print('主进程')
    # 运行结果:jason在运行 jason运行结束 主进程

需注意join()的位置不同 执行的结果也不同

from multiprocessing import Process
import time


def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{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))
    start_time=time.time()
    p1.start()
    p2.start()
    p3.start()  # 此时是三个进程同时启动
    p1.join()  # 等待1s
    p2.join()  # 等待1s(因为上一个进程已经走了1s)
    p3.join()  # 等待1s(因为上一个进程已经走了2s)
    end_time=time.time()
    print(f'总耗时:{end_time-start_time}')
    print('主进程')
    """
    运行结果:
      jason1正在运行
      jason2正在运行
      jason3正在运行
      jason1运行结束
      jason2运行结束
      jason3运行结束
      总耗时:3.2788586616516113
      主进程
    ""

join在中间的代码

from multiprocessing import Process
import time


def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{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))
    start_time=time.time()
    p1.start()
    p1.join()  # 等子进程1结束后再往下执行
    p2.start()
    p2.join()  # 等子进程2结束后再往下执行
    p3.start()
    p3.join()  # 等子进程3结束后再往下执行
    end_time=time.time()
    print(f'总耗时:{end_time-start_time}')
    print('主进程')
    """
    运行结果:
      jason1正在运行
      jason1运行结束
      jason2正在运行
      jason2运行结束
      jason3正在运行
      jason3运行结束
      总耗时:6.6233131885528564
      主进程
    ""

IPC机制(进程间通信)

IPC:进程间通信(同一台计算机间多个进程可以互相交互)
消息队列:临时存储数据的地方 所有人都可以存 也可以取
队列:先进先出 堆栈:后进先出

IPC机制也可以理解为进程间可以基于该队列彼此交互 互相影响,打破了物理隔离

消息队列存在的意义就是为了存数据取数据,一般不需要判断有没有存满

# 【预备知识】:

from multiprocessing import Queue

# 创建队列对象
q = Queue(3)  # 括号内不写默认是2147483647个数据数
# 1.put()往消息队列中添加数据
q.put(111)
q.put(222)
q.put(333)
# 2.full()判断消息队列是否已存满
print(q.full())  # True
# q.put(444)  # 当超出存放极限,程序会处于阻塞态,不报错,直到消息队列中有数据被取出

# 3.get()从消息队列中取数据
# 4.get_nowait()从消息队列中取数据,如果没有了直接报错!
print(q.get())  # 111
print(q.get())  # 222
print(q.get())  # 333
# 5.empty()判断消息队列是否已空 
print(q.empty())  # True
# print(q.get())  # 当超出获取极限,程序会处于阻塞态,不报错,直到队列中有数据被添加

"""
注意:
full() 和 empty() 在多进程下使用会失误
因为假如刚判断完队列有没有满 然后又添加了数据 此时打印出来没满 但是已经新放进去数据了 所以会失误
"""

验证主进程与子进程是否可以利用消息队列交互:

from multiprocessing import Process, Queue

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

if __name__ == '__main__':
    q = Queue()
    p = Process(target=consume, args=(q,))
    p.start()
    q.put('主进程往消息队列中添加了该条数据')
    print('主进程')
    """
    主进程
    子进程获取消息队列中的数据: 主进程往消息队列中添加了该条数据
    """
    # 经过验证发现主进程往消息队列中添加的数据子进程也可以获取到

验证子进程与子进程是否可以利用消息队列交互

from multiprocessing import Process, Queue

def process1(q):
    q.put('子进程1往消息队列中添加了该条数据')
    
def process2(q):
    print('子进程2获取消息队列中的数据:', q.get())

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=process1, args=(q,))
    p2 = Process(target=process2, args=(q,))
    p1.start()
    p2.start()
    print('主进程')
    """
    执行结果:
    主进程
    子进程2获取消息队列中的数据: 子进程1往消息队列中添加了该条数据
    """
    # 经过验证发现子进程1往消息队列中添加的数据 子进程2也可以获取到

生产者消费模型

生产者:负责产生数据的'人'

消费者:负责处理数据的'人'

完整的生产者与消费者模型要有三个部分:
生产者 消息队列 消费者
除了消息队列,只要能提供数据保存服务和提取服务的都可以(数据库、文件等),主要目的就是中间存放数据

如果没有中间存放数据的地方,那生产者生产了一个数据就要马上给消费者,当生产者生产的速度很快,消费者拿数据又很慢时,生产者就得等消费者拿完再去生产,效率太低。所以需要有一个中间存放数据的地方。

进程对象的多种方式

#cmd命令查看进程号的方法: (PID 其实就是ProcessID)
tasklist

#进程号的目的:
方便管理各个进程
(1)查看进程号

print(current_process().pid) 查看自己的进程号
print(current_process()) 查看自己的进程信息
print(os.getpid()) 查看自己的进程号
print(os.getppid()) 查看进程的主进程号

# 方式一:用current_process模块
from multiprocessing import Process, current_process

def task():
    print('子进程查看自己的信息:', current_process())
    print('子进程查看进程号:', current_process().pid)

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    print('主进程查看自己的信息:', current_process())
    print('主进程查看进程号:', current_process().pid)
    """
    执行结果:
    主进程查看自己的信息: <_MainProcess name='MainProcess' parent=None started>
    主进程查看进程号: 20012
    子进程查看自己的信息: <Process name='Process-1' parent=20012 started>
    子进程查看进程号: 20576
    """
# 方式二:用os模块
from multiprocessing import Process
import os

def task():
    print('子进程查看进程号:', os.getpid())
    print('子进程查看主进程的进程号:', os.getppid())

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    print('主进程查看进程号:', os.getpid())
    # print('主进程查看主进程的进程号:', os.getppid())
    # 因为是pycharm启动的进程,所以主进程中的进程号就是pycharm的进程号
    """
    执行结果:
    主进程查看进程号: 20364
    子进程查看进程号: 11108
    子进程查看主进程的进程号: 20364
    """
(2)终止进程

terminate() 终止进程

from multiprocessing import Process
import os

def task():
    print('子进程查看自己的进程号:', os.getpid())

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.terminate()  # 终止子进程
    print('主进程查看自己的进程号:', os.getpid())
    """
    执行结果:
    主进程查看自己的进程号: 6832
    """
    # 虽然执行了子进程,但是又终止了子进程,所以执行结果中没有子进程。如果在终止进程前加个等待时间则可以显示出子进程。
(3)判断进程是否存活

is_alive() 判断进程是否存活(需注意如果在终止后去判断,则需要在判断前加时间等待,给操作系统去终止的时间)

from multiprocessing import Process
import os

def task():
    print('子进程查看自己的进程号:', os.getpid())

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.terminate()  # 终止子进程
    print(p.is_alive())  # 判断子进程是否存活
    print('主进程查看自己的进程号:', os.getpid())
    """
    执行结果:
    True
    主进程查看自己的进程号: 16992
    """
    # 由于终止子进程与判断子进程是否存活都是异步操作,此时提交给操作系统终止继承后还没来得及终止就打印了判断子进程是否存活,所以结果是True
    # 如果想实现终止后再判断,则需要在判断是否存活前加个时间等待,等操作系统终止后再判断
(4)前面的start() 与 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=('torry',))
    p.daemon=True  # 将子进程变成守护进程
    p.start()
    # p.daemon=True  # 不能启动了子进程再设置守护进程!!!
    print('主进程')
    """
    执行结果:
    主进程
    """

僵尸进程和孤儿进程

僵尸进程:进程执行完毕后并不会立刻销毁所有数据,会有一些信息短暂保留下来
如:进程号、进程时间、进程消耗功率等。会把这些信息给父进程查看需要父进程去参与回收

孤儿进程:子进程正常运行 父进程意外结束,那子进程就变成了孤儿进程。
操作系统针对孤儿进程会派遣类似父进程的机制来回收

多进程数据错乱问题

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(f'{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(f'{name}买票成功')
    else:
        print(f'{name}很倒霉 没有抢到票')

def run(name):
    search(name)
    buy(name)

if __name__ == '__main__':
    for i in range(3):
        p = Process(target=run, args=(f'用户{i}',))
        p.start()
        """
        执行结果:
          用户0在查票 当前余票为:0
          用户1在查票 当前余票为:0
          用户2在查票 当前余票为:0
          用户2很倒霉 没有抢到票
          用户0很倒霉 没有抢到票
          用户1很倒霉 没有抢到票
        """

以上发现多进程在操作数据时可能会造成数据错乱问题,只有用互斥锁才可以完美解决

互斥锁的功能:将并发改为串行(单道),牺牲了效率但是保障了数据的安全

标签:__,name,周末,编程,print,并发,进程,import,data
From: https://www.cnblogs.com/lvqingmei/p/16909826.html

相关文章

  • 05.高并发秒杀公平不超卖-消息MQ架构体系(2)
                                                         ......
  • 11月第3次周末总结
    11月第3次周末总结软件开发架构C\S架构client客户端server服务端优点客户端可以高度定制化功能缺点:需要下载B\S架构browser客户端server服务端优点:不需要下......
  • Java 函数式编程「二」
    接上回,聊聊函子functor。functor是一个容器。该容器的value属性指向被包裹的数据;该容器的map方法对容器进行映射变换。以下代码实现一个最普通的functor,称之为J......
  • 项目实战:并发下保证接口的幂等性
    1.1幂等性的概念Methodscanalsohavethepropertyof"idempotence"inthat(asidefromerrororexpirationissues)theside-effectsofN>0identicalrequ......
  • 从Scratch 2学到的一些编程思想
    从Scratch2学到的一些编程思想所有程序都是事件驱动的mingdu.zhengatgmaildotcomScratch是麻省理工大学(MIT)为8~16岁的孩子开发的编程工具。Scratch的基本原理是为所选......
  • 嵌入式软件异步编程:同步还是异步?
    异步并不总是提高效率的,那什么时候用同步,什么时候用异步呢?mingdu.zhengatgmaildotcom异步要解决的是CPU和I/O速度差异的问题,所以异步适合I/O远慢于CPU的情况,如果I/O仅稍......
  • 并发编程理论和进程理论
    目录一、并发编程理论操作系统发展史1、手工操作——穿孔卡片2、批处理——磁带存储1.联机批处理系统2.脱机批处理系统二、多道程序设计技术单道技术多道技术多道技术......
  • socket模块实现网络编程及struct模块解决黏包问题
    目录一、socket模块1、简介2、基于文件类型的套接字家族3、基于网络类型的套接字家族二、socket代码简介三、socket代码优化1.聊天内容自定义2.让聊天循环起来3.用户输入的......
  • 十大排序算法的各种编程语言的实现
    排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排......
  • 并发编程 2 进程
    同步与异步#用来表达任务的提交方式同步:提交完任务之后原地等待任务的返回结果,期间不做任何事异步:提交完任务之后不愿地等待任务的返回结果,直接去做其他事,有......