1、线程就是CPU调度的最小单位。
2、 # 线程进程之间的对比
# 线程不能独立存在,必须在一个进程里
# 线程的开启 关闭以及切换的开销要远远小于进程
import time from threading import Thread from multiprocessing import Process def func(i): i += 1 if __name__ == '__main__': start_time = time.time() t_l = [] for i in range(50): t = Thread(target=func, args=(i,)) t.start() t_l.append(t) for t in t_l: t.join() print('主线程') print(time.time() - start_time) if __name__ == '__main__': start_time = time.time() p_l = [] for i in range(50): p = Process(target=func, args=(i,)) p.start() p_l.append(t) for p in t_l: p.join() print('主进程') print(time.time() - start_time)
3、 # 同一个进程之间的多个线程之间数据共享
# 全局解释器锁GIL
# 使得一个进程中的多个线程不能充分的利用多核
# 这个锁能保证同一时刻只有一个线程运行
4、
# 主线程如果结束了 那么整个进程就结束
# 守护线程 会等待主线程结束之后才结束.
# 主进程 等待 守护进程 子进程
# 守护进程 只守护主进程的代码就可以了
# 守护线程不行 主线程如果结束了 那么整个进程就结束 所有的线程就都结束
import time from threading import Thread def thread1(): while True: print(True) time.sleep(0.5) def thread2(): print('in t2 start') time.sleep(3) print('in t2 end') if __name__ == '__main__': t1 = Thread(target=thread1) t1.setDaemon(True) t1.start() t2 = Thread(target=thread2) t2.start() time.sleep(1) print('主线程')
# 线程池
# concurrent.futures.ThreadPoolExecutor
# 线程池对象.map(func,iterable)
# 线程池对象.submit 异步提交
# 线程池对象.shutdown ==>close+join
# 任务对象.result
# 任务对象.add_done_callback 回调函数都是在子线程执行
# 进程池
# concurrent.futures.ProcessPoolExecutor
# 任务对象.add_done_callback 回调函数都是在主进程执行
# 协程
# 在一个线程内的多个任务之间能够互相切换
# 这个切换不是操作系统级别的 都是用户控制的
# 最简单的切换以及状态的保留使用python原生的语法yield其实就可以实现
# 但是一般情况下我们都是用gevent来完成协程的工作
# 它能够帮助我们规避IO操作
# 它内部使用的协程机制是greenlet
# 协程比起线程的好处
# 充分的利用了一条线程来提高CPU的工作效率
# 不存在数据不安全的问题
# join() 在主线程中没有其他阻塞事件的时候就是用join来保证协程任务都能顺利的执行完
# spawn() 发起一个协程任务
IO 模型
# IO多路复用 - 操作系统提供的
# 1.程序不能干预过程
# 2.操作系统之间的差异
import select
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
sk.setblocking(False)
rlst = [sk]
while True:
rl,wl,xl = select.select(rlst,[],[]) #[sk,conn1,conn2]
# 为什么突然把sk返回回来了? sk对象有数据可以被读了
# 为什么返回三个列表? 读事件的列表 写事件的列表 条件的列表
# 为什么是列表? 有可能同时有多个被监听的对象发生读时间
for obj in rl:
if obj is sk: # is的意思更精准,判断的是obj就是sk
conn,addr = obj.accept()
rlst.append(conn)
else:
try:
ret = obj.recv(1024)
print(ret)
obj.send(b'hello')
except ConnectionResetError:
obj.close()
rlst.remove(obj)
# TCP协议来说,如果对方关闭了连接
# 另一方有可能继续 接收 空消息 或者 报错
# 背代码
# 将具体的情况套到代码中 将逻辑理顺
# 理解之前IO多路复用的那张图
# 什么叫IO多路复用
# io多路复用是操作系统提供的一种 监听 网络IO操作的机制
# 监听三个列表
# 当某一个列表有对应的事件发生的时候
# 操作系统通知应用程序
# 操作系统根据返回的内容做具体的操作
# 对于只有一个对象需要监听的情况 IO多路复用并无法发挥作用
# 对于并发接收网络请求的应用场景 IO多路复用可以帮助你在节省CPU利用率和操作系统调用的基础上完成并发需求
# IO多路复用
# select 是windows上的机制 轮询的方式来监听每一个对象是否有对应的事件发生的,数据越多延迟越大
# 能够处理的对象数是有限的
# poll linux 和select的机制基本一致,对底层存储被监听对象的数据结构做了优化
# 能够处理的对象个数增加了
# epoll linux 采用了回调函数的方式来通知应用被监听的对象有事件发生了