线程、协成、IO模型

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 采用了回调函数的方式来通知应用被监听的对象有事件发生了
原文地址:https://www.cnblogs.com/zhaosijia/p/9391922.html