目录
- 一、软件开发架构
- 1. C/S
- 2. B/S
- 3. 客户端与服务端作用
- 二、网络编程
- 1. 互联网协议OSI七层协议
- 1.1 物理连接层
- 1.2 数据链路层
- 1.3 网络层
- 1.4 传输层
- 1.5 应用层
- 2. socket
- 3. subprocess(补充)
- 4. 粘包问题
- 5. struct解决粘包问题
- 6. socketserver
- 1. 互联网协议OSI七层协议
- 三、并发编程
- 1. 多道技术
- 1.1 单道
- 1.2 多道
- 2. 并发与并行
- 3. 进程
- 3.1 程序与进程的区别
- 3.2 进程调度
- 3.3 进程的三个状态
- 3.4 同步与异步
- 3.5 阻塞与非阻塞
- 3.6 创建进程的两种方式
- 3.7 回收进程资源的两种条件
- 4. 僵尸进程与孤儿进程(了解)
- 5. 守护进程
- 6. 互斥锁
- 7. 队列(进程队列)
- 8. 堆栈
- 9. IPC进程间通信
- 10. 生产者与消费者 模型
- 11. 线程
- 11.1 什么是线程
- 11.2 使用线程的好处
- 11.3 进程与线程优缺点
- 12. 线程间数据是共享的
- 13. GIL全局解释器锁
- 14. 多线程使用的好处
- 15. 死锁现象(了解)
- 16. 递归锁(了解)
- 17. 信号量(了解)
- 18. 线程队列
- 19. event事件
- 20. 进程池与线程池
- 21. 协程
- 21.1 单线程下实现并发的好处
- 21.2 协程的创建
- 22. gevent
- 23. IO模型(了解)
- 1. 多道技术
一、软件开发架构
1. C/S
Client:客户端
Server:服务端
优点:占用网络资源更少,软件的使用更稳定。
缺点:使用过程需要下载、更新,不能直接使用,且不能同时使用多个软件。
2. B/S
Browser:浏览器端(客户端)
Server:服务端
优点:无需下载更新,可以直接使用,且可以打开多个网页,用时访问多个网站。
缺点:消耗网络资源太大,如果网络不稳定,浏览器会受到很大影响。
3. 客户端与服务端作用
服务端:24小时不间断提供服务。
客户端:需要使用服务时,可以随时取用。
二、网络编程
1. 互联网协议OSI七层协议
- 应用层
- 表示层
- 会话层
- 传输层
- 网络层
- 数据链路层
- 物理连接层
1.1 物理连接层
作用:基于电信号发送二进制数据。
1.2 数据链路层
由来:单纯的二进制信号没有意义,必须规定好二进制的意思,即多少个二进制为一组,每组什么意思。
作用:通过“以太网协议”定义了电信号的分组方式。
以太网协议:统一的给电信号的标准,规定电信号构成的数据包必须包含报头和数据。
报头:包含发送者(原地址),接受者(目标地址),及数据类型。
网卡:从来接收分析二进制信号。
mac地址:每块网卡出厂时都会被烧上世界上唯一的MAC地址,由12位16进制数组成(前六位为厂商编号,后六位为流水线号)。
广播:多对多通信(大吼一声)。但不能跨局域网通信,且容易产生广播风暴。
1.3 网络层
由来:需要一种方法来区分那些计算机属于同一广播域。
功能:引入一套新的地址用来区分不同的广播域和子网。
IP:定位局域网的位置。
arp协议:广播的方式发送数据包,获取mac地址,并解析成IP和port。
1.4 传输层
port:用来定位一台计算机上的唯一一个应用程序。
TCP协议
一种流式协议。
若想在TCP协议下通信,必须建立连接,并建立双向通道。
三次握手件连接:
- 客户端往服务端发送请求建立通道;
- 服务端确认客户端的请求,并往客户端也发送建立通道请求;
- 客户端接收到服务端建立连接的请求,并返回确认。
双向通道:
- 客户端往服务端发送请求获取数据,服务端务必返回数据,客户端确认收到。
- 若服务端没有返回数据,客户端则会反复发送,一直到某个时间段内,会停止发送。
四次挥手短连接:
- 客户端向服务端发送断开连接请求,断开C2S的单向连接;
- 服务端返回确认请求,并把未完成传输的数据传输完成;
- 服务端向客户端发送断开连接请求;
- 客户端返回确认信息,剩余的一条S2C的连接断开。
UDP协议
一种不可靠的传输协议,有以下特点:
- 不需要建立双线通道;
- 不会有粘包问题;
- 传输速度快;
- 数据不安全;
- 客户端发送数据,不需要服务端确认收到,爱收不收。
TCP协议与UDP协议的区别
TCP:像在打电话,你侬我侬;
UDP:像在发短信,发了就不管了。
1.5 应用层
- ftp
- http:可以携带一堆数据
- https:http + ssl
2. socket
socket用来写套接字客户端与服务端的模块,内部帮我们封装好了7层协议需要做的事情。
3. subprocess(补充)
通过代码往cmd创造一个管道,并且发送命令和接收cmd返回结果。
4. 粘包问题
发生的原因:
- 不能确定对方发送数据的大小;
- 在短时间内,间隔时间短,并且数据量小的情况,系统会自动将这些数据打包成一个多次发送的数据,然后一次性发送给接收端。
5. struct解决粘包问题
通过struct模块,将数据的长度这个数据压缩成一个固定长度的报头,发送这个报头后,接收方可以通过这个报头解析出真实发送数据的长度,在通过这个长度接受数据,就不会粘包了。
6. socketserver
- 可以支持并发的一种socket优化模块(没感觉有什么优化)
三、并发编程
1. 多道技术
1.1 单道
程序串行执行,同步执行
1.2 多道
CPU切换程序并保存状态执行多个程序
空间上的复用
支持多个程序同时运行
时间上的复用
- 遇到IO操作就会切换程序
- 程序占用CPU时间过长会切换
2. 并发与并行
并行:看上去像多个程序同时执行,实际上只是每一个时间点都只运行了一个程序。即多道技术。
并行:真正意义的多个程序同时执行,需要多个CPU,每一个时间点有多个程序在运行。多核技术。
3. 进程
表示正在进行的一个过程,是一种资源单位,每创建一个都会生成一个名称空间,
3.1 程序与进程的区别
程序:就是一堆代码
进程:就是一堆代码运行的过程
3.2 进程调度
目前使用的基本调度方法为:时间片轮转发+分级反馈队列法
- 时间片轮转法:将固定时间等分成进程数量的时间片,将每一个时间片分配给每一个进程。
- 分级反馈队列:时间短的进程进入一级队列,优先运行,时间慢一点的进入二级队列,一级运行完了再运行。。。以此类推。
3.3 进程的三个状态
就绪态
创建多个进程,必须要排队准备好运行
运行态
进程开始运行,之后有两种可能:1. 运行结束;2. 遇到IO,进入阻塞态
阻塞态
但遇到IO操作,进入阻塞态,IO结束,进入就绪态,等待下一次开始运行。
3.4 同步与异步
指提交任务的方式。
同步:同步提交,串行,一个任务结束后,另一个任务才能提交并执行。
异步:异步提交,并发,无需等待上个任务结束就可提交任务开始,并发执行。
3.5 阻塞与非阻塞
- 阻塞:阻塞态
- 非阻塞:就绪态与运行态
3.6 创建进程的两种方式
3.6.1 第一种
p = Process(target=任务, args=(任务的参数, ))
p.daemon = True # 守护进程,必须放在start()前,否则报错
p.start() # 向操作系统提交创建进程的任务
p.join() # 向操作系统发送请求, 等所有子进程结束,父进程再结束
3.6.2 第二种
class MyProcess(Process):
def run(self): # self == p
任务的过程
p = MyProcess()
p.daemon = True # 守护进程,必须放在start()前,否则报错
p.start() # 向操作系统提交创建进程的任务
p.join() # 向操作系统发送请求, 等所有子进程结束,父进程再结束
3.7 回收进程资源的两种条件
- 调用join让子进程结束后,主进程才能正常结束
- 主进程自然结束死亡
4. 僵尸进程与孤儿进程(了解)
僵尸进程: 凡是子进程结束后,PID号还在, 主进程意外死亡,没法给子进程回收资源.
- 每个子进程结束后,都会变成,僵尸进程 (PID)
孤儿进程: 凡是子进程没有结束,但是主进程意外死亡.操作系统优化机制(孤儿院),会将没有主,并且存活的进程,在该进程结束后回收资源.
5. 守护进程
一种规则,可以让子进程在父进程结束后二必须结束。
6. 互斥锁
将并发的程序变成并行,保护了数据的安全,不会混乱,但牺牲了执行效率。
from multiprocessing import Lock
mutex = Lock()
# 加锁
mutex.acquire()
修改数据
mutex.release()
7. 队列(进程队列)
# FIFO队列: 先进先出
from multiprocessing import Queue
q = Queue(5) # 括号内的数字表示队列内可以添加的最大数据量
# 添加数据,若队列添加数据满了,则等待
q.put()
# 添加数据,若队列添加数据满了,直接报错
q.put_nowait()
# 获取队列中的数据
q.get() # 若队列中没数据,会卡住等待
q.get_nowait() # 若队列中没数据,会直接报错
8. 堆栈
LIFO队列。
9. IPC进程间通信
- 进程间数据是隔离的。
- 队列可以让进程间进行通信
10. 生产者与消费者 模型
生产者:生产数据
消费者:使用数据
为了保证供需平衡。
11. 线程
11.1 什么是线程
线程:执行单位
- 创建进程时,会自带一个线程
进程:资源单位
一个进程下可以创建多个线程。
11.2 使用线程的好处
节省资源的开销
11.3 进程与线程优缺点
- 进程:
- 优点:
- 多核下可以并行执行
- 计算密集型下可以提高效率
- 缺点:
- 开销资源远高于线程
- 优点:
- 线程:
- 优点:
- 占用资源远比进程小
- IO密集型下提高效率
- 缺点:
- 无法利用多核优势
- 优点:
12. 线程间数据是共享的
同一个进程下可以创建多个子线程,这些子线程可以共用该进程的内存空间,因此,线程间的数据是共享的。
13. GIL全局解释器锁
声明:只有CPython才自带一个GIL全局解释器锁。
- GIL的本质是一个互斥锁。
- GIL的目的是为了阻止同一个进程内多个线程同时执行(并行)。
- 单个进程下的多个线程无法实现并行,但能实现并发。
- 这把锁主要是因为cpython的内存管理不是“线程安全”的。
- 内存管理:垃圾回收机制。。
注意:多个线程过来执行,一旦遇到IO操作,就会立马释放该线程的GIL解释器锁,交给下一个进程使用。
14. 多线程使用的好处
- 多线程:提高IO密集型程序的执行效率
- 多进程:提高计算密集型程序的执行效率
15. 死锁现象(了解)
a进程用了a锁,b进程用了b锁,这时候a进程还想用b锁,b进程还想用a锁,就会导致死锁。
16. 递归锁(了解)
解决死锁现象
mutex = Lock() # 只能引用1次
mutex1, mutex2 = RLock() # 可以引用多次
+1, 只要这把锁计数为0释放该锁, 让下一个人使用, 就不会出现死锁现象.
17. 信号量(了解)
信号量可以理解为一个锁池,可以让池里的这个锁给多个任务一起使用。
sm = Semaphore(5) 可以让5个任务使用
18. 线程队列
使用场景:若想在线程间数据不安全情况下使用线程队列,可以保证线程间数据的安全
import queue
- FIFO: 先进先出队列
queue.Queue()
- LIFO: 后进先出队列
queue.LifoQueue()
- 优先级队列:
- 根据数字大小判断,判断出队优先级.
- 进队数据是无序的
queue.PriorityQueue()
19. event事件
可以控制线程的执行,让一些线程控制另一些线程的执行。
e = Event()
- 线程1
e.set() # 给线程2发送信号,让他执行。
- 线程2
e.wait() # 等待线程1的信号。
20. 进程池与线程池
为了控制进程/线程创建的数量,保证了硬件能正常运行。
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
pool1 = ProcessPoolExecutor() # 默认CPU个数
pool2 = ThreadPoolExecutor() # CPU个数 * 5
pool3 = ProcessPoolExecutor(100) # 100个
pool4 = ThreadPoolExecutor(200) # 200个
# 将函数地址的执行结果,给回调函数
pool4.submit(函数地址, 参数).add_done_callback(回调函数地址)
- 回调函数(必须接收一个参数res):
# 获取值
res2 = res.result()
21. 协程
协程:单线程下实现并发,不是任何的单位,是程序员YY出来的名字。
进程:资源单位
线程:执行单位
21.1 单线程下实现并发的好处
节省资源,单线程<多线程<多进程
- IO密集型下:协程有优势
- 计算密集型下:进程有优势
- 高并发:多进程+多线程+协程 (Nginx)
21.2 协程的创建
手动实现切换+保存状态:
- yield
- 函数一直在调用next()
- 会不停的切换
yield不能监听IO操作的任务,可以使用gevent来实现监听IO操作。
22. gevent
pip3 install gevent
from gevent import monkey
monkey.patch_all() # 设置监听所有IO
from gevent import spawn, joinall # 实现 切换 + 保存状态
- 实现了单线程下实现并发
s1 = spawn(任务1)
s2 = spawn(任务2)
joinall([s1, s2])
23. IO模型(了解)
- 阻塞IO
- 非阻塞IO
- 多路复用IO
- 异步IO