目录
Client:客户端
Server:服务端
优点:占用网络资源更少,软件的使用更稳定。
缺点:使用过程需要下载、更新,不能直接使用,且不能同时使用多个软件。
Browser:浏览器端(客户端)
Server:服务端
优点:无需下载更新,可以直接使用,且可以打开多个网页,用时访问多个网站。
缺点:消耗网络资源太大,如果网络不稳定,浏览器会受到很大影响。
服务端:24小时不间断提供服务。
客户端:需要使用服务时,可以随时取用。
作用:基于电信号发送二进制数据。
由来:单纯的二进制信号没有意义,必须规定好二进制的意思,即多少个二进制为一组,每组什么意思。
作用:通过“以太网协议”定义了电信号的分组方式。
以太网协议:统一的给电信号的标准,规定电信号构成的数据包必须包含报头和数据。
报头:包含发送者(原地址),接受者(目标地址),及数据类型。
网卡:从来接收分析二进制信号。
mac地址:每块网卡出厂时都会被烧上世界上唯一的MAC地址,由12位16进制数组成(前六位为厂商编号,后六位为流水线号)。
广播:多对多通信(大吼一声)。但不能跨局域网通信,且容易产生广播风暴。
由来:需要一种方法来区分那些计算机属于同一广播域。
功能:引入一套新的地址用来区分不同的广播域和子网。
IP:定位局域网的位置。
arp协议:广播的方式发送数据包,获取mac地址,并解析成IP和port。
port:用来定位一台计算机上的唯一一个应用程序。
一种流式协议。
若想在TCP协议下通信,必须建立连接,并建立双向通道。
三次握手件连接:
双向通道:
四次挥手短连接:
一种不可靠的传输协议,有以下特点:
TCP:像在打电话,你侬我侬;
UDP:像在发短信,发了就不管了。
socket用来写套接字客户端与服务端的模块,内部帮我们封装好了7层协议需要做的事情。
通过代码往cmd创造一个管道,并且发送命令和接收cmd返回结果。
发生的原因:
通过struct模块,将数据的长度这个数据压缩成一个固定长度的报头,发送这个报头后,接收方可以通过这个报头解析出真实发送数据的长度,在通过这个长度接受数据,就不会粘包了。
程序串行执行,同步执行
CPU切换程序并保存状态执行多个程序
空间上的复用
支持多个程序同时运行
时间上的复用
并行:看上去像多个程序同时执行,实际上只是每一个时间点都只运行了一个程序。即多道技术。
并行:真正意义的多个程序同时执行,需要多个CPU,每一个时间点有多个程序在运行。多核技术。
表示正在进行的一个过程,是一种资源单位,每创建一个都会生成一个名称空间,
程序:就是一堆代码
进程:就是一堆代码运行的过程
目前使用的基本调度方法为:时间片轮转发+分级反馈队列法
就绪态
创建多个进程,必须要排队准备好运行
运行态
进程开始运行,之后有两种可能:1. 运行结束;2. 遇到IO,进入阻塞态
阻塞态
但遇到IO操作,进入阻塞态,IO结束,进入就绪态,等待下一次开始运行。
指提交任务的方式。
同步:同步提交,串行,一个任务结束后,另一个任务才能提交并执行。
异步:异步提交,并发,无需等待上个任务结束就可提交任务开始,并发执行。
p = Process(target=任务, args=(任务的参数, ))
p.daemon = True # 守护进程,必须放在start()前,否则报错
p.start() # 向操作系统提交创建进程的任务
p.join() # 向操作系统发送请求, 等所有子进程结束,父进程再结束
class MyProcess(Process):
def run(self): # self == p
任务的过程
p = MyProcess()
p.daemon = True # 守护进程,必须放在start()前,否则报错
p.start() # 向操作系统提交创建进程的任务
p.join() # 向操作系统发送请求, 等所有子进程结束,父进程再结束
僵尸进程: 凡是子进程结束后,PID号还在, 主进程意外死亡,没法给子进程回收资源.
孤儿进程: 凡是子进程没有结束,但是主进程意外死亡.操作系统优化机制(孤儿院),会将没有主,并且存活的进程,在该进程结束后回收资源.
一种规则,可以让子进程在父进程结束后二必须结束。
将并发的程序变成并行,保护了数据的安全,不会混乱,但牺牲了执行效率。
from multiprocessing import Lock
mutex = Lock()
# 加锁
mutex.acquire()
修改数据
mutex.release()
# FIFO队列: 先进先出
from multiprocessing import Queue
q = Queue(5) # 括号内的数字表示队列内可以添加的最大数据量
# 添加数据,若队列添加数据满了,则等待
q.put()
# 添加数据,若队列添加数据满了,直接报错
q.put_nowait()
# 获取队列中的数据
q.get() # 若队列中没数据,会卡住等待
q.get_nowait() # 若队列中没数据,会直接报错
LIFO队列。
生产者:生产数据
消费者:使用数据
为了保证供需平衡。
线程:执行单位
进程:资源单位
一个进程下可以创建多个线程。
节省资源的开销
同一个进程下可以创建多个子线程,这些子线程可以共用该进程的内存空间,因此,线程间的数据是共享的。
声明:只有CPython才自带一个GIL全局解释器锁。
注意:多个线程过来执行,一旦遇到IO操作,就会立马释放该线程的GIL解释器锁,交给下一个进程使用。
a进程用了a锁,b进程用了b锁,这时候a进程还想用b锁,b进程还想用a锁,就会导致死锁。
解决死锁现象
mutex = Lock() # 只能引用1次
mutex1, mutex2 = RLock() # 可以引用多次
+1, 只要这把锁计数为0释放该锁, 让下一个人使用, 就不会出现死锁现象.
信号量可以理解为一个锁池,可以让池里的这个锁给多个任务一起使用。
sm = Semaphore(5) 可以让5个任务使用
使用场景:若想在线程间数据不安全情况下使用线程队列,可以保证线程间数据的安全
import queue
- FIFO: 先进先出队列
queue.Queue()
- LIFO: 后进先出队列
queue.LifoQueue()
- 优先级队列:
- 根据数字大小判断,判断出队优先级.
- 进队数据是无序的
queue.PriorityQueue()
可以控制线程的执行,让一些线程控制另一些线程的执行。
e = Event()
e.set() # 给线程2发送信号,让他执行。
e.wait() # 等待线程1的信号。
为了控制进程/线程创建的数量,保证了硬件能正常运行。
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()
协程:单线程下实现并发,不是任何的单位,是程序员YY出来的名字。
进程:资源单位
线程:执行单位
节省资源,单线程<多线程<多进程
手动实现切换+保存状态:
yield不能监听IO操作的任务,可以使用gevent来实现监听IO操作。
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])
原文:https://www.cnblogs.com/bowendown/p/11748516.html