IO阻塞


每日测验

  • 简述死锁现象

  • 你用过哪些队列

  • 阐述进程池线程池概念及基本使用

  • 什么是协程,如何简单实现

昨日内容回顾

  • 死锁现象

    """
    即便你知道如何抢锁释放锁 也极有可能造成程序的死锁现象
    
    后续我们在写项目的时候 也不会自己去处理锁的问题 都是底层封装好的
    所以你不用担心
    """
    
  • 递归锁

    """
    它也是一把互斥锁,但是它可以被第一个抢到它的人连续的acquire和release
    每acquire一次内部有一个引用计数加1
    每release一次内部有一个引用计数减1
    只要引用计数不为0 永远也无法被其他人抢到
    """
    
  • 信号量

    """
    信号量在不同的领域和知识阶段可能对应不同的概念
    如果将互斥锁比喻成一个厕所 那么信号量就相当于多个厕所
    """
    
  • event事件

    """
    一些线程/进程等待另外一些线程/进程发送可以运行的信号 才开始运行
    from threading import Event
    e = Event()
    
    # 等待
    e.wait()
    # 发送信号 
    e.set()
    """
    
  • 各种队列

    """
    1 常见队列 queue
    	先进先出
    	q = queue.Queue()
    	q.put()
    	q.get(timeout=3)
    	q.get_nowait()
    	q.full()
    	q.empty()
    
    2 后进先出 LifoQueue()
    	q = queue.LifoQueue()
    	q.put()
    	q.get()
    
    3 优先级 PriorityQueue()
    	q = queue.PriorityQueue()
    	q.put((10,'data'))
    	q.put((-5,'data'))
    	元祖里面的第一个参数是数字 并且支持负数
    	数字越小优先级越高
    """
    
  • 进程池线程池

    """
    硬件的发展肯定是赶不上软件的开发速度的
    	
    思考我们以前借助于开设进程和线程的方式来实现TCP服务端的并发
    	每来一个客户端就开设一个进程或者线程
    
    无论是开设进程还是开设线程其实都需要消耗一定的资源
    
    我们应该在保证计算机硬件安全的情况下,最大限度的利用计算机
    
    池的概念
    	它的出现是为了保证计算机硬件的安全
    	降低了程序的运行效率 但是保证了计算机硬件安全
    """
    # 进程池线程池都不需要我们自己去造 直接使用封装好的模块
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    # 1 生成进程池线程池
    pool1 = ThreadPoolExecutor()  # 不填默认是cpu个数的五倍
    pool2 = ProcessPoolExecutor()  # 不填默认就是cpu的个数
    
    # 2 朝池子中提交任务
    pool1.submit(task,args...)  # 异步提交
    
    # 3 submit其实会返回一个Future类的对象 该对象调用result就能获取到任务的结果
    res = pool1.submit(task,args...) 
    res.result()  # 同步
    
    # 4 池子对象的方法
    pool1.shotdown()  # 关闭池子 等待池子中所有的任务运行结束 再继续往后执行代码
    
    # 5 异步回调机制
    """给每一个异步提交的任务绑定一个方法,一旦任务有结果了会立刻自动触发该方法"""
    pool1.submit(task,args).add_done_callback(call_back)
    # 注意异步回调函数拿到的也是一个对象 
    
  • 协程

    """
    单线程下实现并发
    这个概念完全是我们程序员自己想出来
    
    多道技术
    	切换+保存状态
    
    我们想通过代码层面自己检测IO行为。一旦遇到IO代码层面实现切换
    这样给操作系统的感觉好像我这个程序一直运行没有IO
    欺骗操作系统从而最大化的利用CPU
    
    一味的切换加保存状态也有可能会降低程序的效率
    计算密集型的  不行
    IO密集型的    可以
    """
    
  • gevent模块

    # 该模块能够帮我们检测IO并实现切换
    from gevent import monkey;monkey.patch_all()
    from gevent import spawn
    
    # spawn在检测的时候 是异步提交的
    spawn(server).join()
    g = spawn(server)
    g.join
    
  • 基于协程实现TCP服务端单线程下的并发

    # 代码稍微搂一眼 感受它的牛逼之处
    

总结

"""
多进程下面开设多线程
多线程下面再利用协程
最大长度的提升软件运行的效率
"""

今日内容概要

  • IO模型

  • 复习网络和并发知识点

  • 后期课程安排

IO模型简介

"""
我们这里研究的IO模型都是针对网络IO的
Stevens在文章中一共比较了五种IO Model:
    * blocking IO           阻塞IO
    * nonblocking IO      非阻塞IO
    * IO multiplexing      IO多路复用
    * signal driven IO     信号驱动IO
    * asynchronous IO    异步IO
    由signal driven IO(信号驱动IO)在实际中并不常用,所以主要介绍其余四种IO Model。
"""
#1)等待数据准备 (Waiting for the data to be ready)
#2)将数据从内核拷贝到进程中(Copying the data from the kernel to the process)

同步异步
阻塞非阻塞
常见的网络阻塞状态:
  	accept
    recv
    recvfrom
    
    send虽然它也有io行为 但是不在我们的考虑范围

阻塞IO模型

"""
我们之前写的都是阻塞IO模型  协程除外
"""
import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)


while True:
    conn, addr = server.accept()
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:break
            print(data)
            conn.send(data.upper())
        except ConnectionResetError as e:
            break
    conn.close()
    
# 在服务端开设多进程或者多线程 进程池线程池 其实还是没有解决IO问题	
该等的地方还是得等 没有规避
只不过多个人等待的彼此互不干扰

非阻塞IO

"""
要自己实现一个非阻塞IO模型
"""
import socket
import time


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)
server.setblocking(False)
# 将所有的网络阻塞变为非阻塞
r_list = []
del_list = []
while True:
    try:
        conn, addr = server.accept()
        r_list.append(conn)
    except BlockingIOError:
        # time.sleep(0.1)
        # print('列表的长度:',len(r_list))
        # print('做其他事')
        for conn in r_list:
            try:
                data = conn.recv(1024)  # 没有消息 报错
                if len(data) == 0:  # 客户端断开链接
                    conn.close()  # 关闭conn
                    # 将无用的conn从r_list删除
                    del_list.append(conn)
                    continue
                conn.send(data.upper())
            except BlockingIOError:
                continue
            except ConnectionResetError:
                conn.close()
                del_list.append(conn)
        # 挥手无用的链接
        for conn in del_list:
            r_list.remove(conn)
        del_list.clear()

# 客户端
import socket


client = socket.socket()
client.connect(('127.0.0.1',8081))


while True:
    client.send(b'hello world')
    data = client.recv(1024)
    print(data)

总结

"""
虽然非阻塞IO给你的感觉非常的牛逼
但是该模型会	长时间占用着CPU并且不干活 让CPU不停的空转
我们实际应用中也不会考虑使用非阻塞IO模型

任何的技术点都有它存在的意义 
实际应用或者是思想借鉴
"""

IO多路复用

"""
当监管的对象只有一个的时候 其实IO多路复用连阻塞IO都比比不上!!!
但是IO多路复用可以一次性监管很多个对象

server = socket.socket()
conn,addr = server.accept()

监管机制是操作系统本身就有的 如果你想要用该监管机制(select)
需要你导入对应的select模块
"""
import socket
import select


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)
read_list = [server]


while True:
    r_list, w_list, x_list = select.select(read_list, [], [])
    """
    帮你监管
    一旦有人来了 立刻给你返回对应的监管对象
    """
    # print(res)  # ([], [], [])
    # print(server)
    # print(r_list)
    for i in r_list:  #
        """针对不同的对象做不同的处理"""
        if i is server:
            conn, addr = i.accept()
            # 也应该添加到监管的队列中
            read_list.append(conn)
        else:
            res = i.recv(1024)
            if len(res) == 0:
                i.close()
                # 将无效的监管对象 移除
                read_list.remove(i)
                continue
            print(res)
            i.send(b'heiheiheiheihei')

 # 客户端
import socket


client = socket.socket()
client.connect(('127.0.0.1',8080))


while True:

    client.send(b'hello world')
    data = client.recv(1024)
    print(data)

总结

"""
监管机制其实有很多
select机制  windows linux都有

poll机制    只在linux有   poll和select都可以监管多个对象 但是poll监管的数量更多

上述select和poll机制其实都不是很完美 当监管的对象特别多的时候
可能会出现 极其大的延时响应

epoll机制   只在linux有
	它给每一个监管对象都绑定一个回调机制
	一旦有响应 回调机制立刻发起提醒

针对不同的操作系统还需要考虑不同检测机制 书写代码太多繁琐
有一个人能够根据你跑的平台的不同自动帮你选择对应的监管机制
selectors模块
"""

异步IO

"""
异步IO模型是所有模型中效率最高的 也是使用最广泛的
相关的模块和框架
	模块:asyncio模块
	异步框架:sanic tronado twisted
		速度快!!!
"""
import threading
import asyncio


@asyncio.coroutine
def hello():
    print('hello world %s'%threading.current_thread())
    yield from asyncio.sleep(1)  # 换成真正的IO操作
    print('hello world %s' % threading.current_thread())


loop = asyncio.get_event_loop()
tasks = [hello(),hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

四个IO模型对比

参考博客园图解,稍微了解即可

网络并发知识点梳理

  • 软件开发架构

  • 互联网协议

    """
    osi七层
    五层
    每一层都是干嘛的
    	以太网协议  广播风暴
    	IP协议
    	
    	TCP/UDP
    """
    
  • 三次握手四次挥手

  • socket简介

  • TCP黏包问题 定制固定长度的报头

  • UDP协议

  • socketserver模块

  • 操作系统发展史

  • 多道技术

  • 进程理论

  • 开启进程的两种方式

  • 互斥锁

  • 生产者消费者模型

  • 线程理论

  • 开启线程的两种方式

  • GIL全局解释器锁

  • 进程池线程池

  • 协程的概念

  • IO模型的了解