zoukankan      html  css  js  c++  java
  • IO并发模型

    IO 分类

    IO分类:阻塞IO ,非阻塞IO,IO多路复用,异步IO等

    阻塞IO

    1.定义:在执行IO操作时如果执行条件不满足则阻塞。阻塞IO是IO的默认形态。

    2.效率:阻塞IO是效率很低的一种IO。但是由于逻辑简单所以是默认IO行为。

    3.阻塞情况:

    • 因为某种执行条件没有满足造成的函数阻塞
      e.g. accept input recv

    • 处理IO的时间较长产生的阻塞状态
      e.g. 网络传输,大文件读写

    非阻塞IO

    1.定义 :通过修改IO属性行为,使原本阻塞的IO变为非阻塞的状态。

    设置套接字为非阻塞IO

    sockfd.setblocking(bool)

    • 功能:设置套接字为非阻塞IO
    • 参数:默认为True,表示套接字IO阻塞;设置为False则套接字IO变为非阻塞

    超时检测 :设置一个最长阻塞时间,超过该时间后则不再阻塞等待。

    sockfd.settimeout(sec)

    • 功能:设置套接字的超时时间
    • 参数:设置的时间

     1 from socket import *
     2 from time import sleep,ctime
     3 
     4 # 日志文件
     5 f = open('log.txt','a+')
     6 
     7 # 创建套接字
     8 sockfd = socket()
     9 sockfd.bind(('127.0.0.1',9999))
    10 sockfd.listen(3)
    11 
    12 # 设置套接字为非阻塞
    13 sockfd.setblocking(False)
    14 
    15 # 设置超时检测时间
    16 sockfd.settimeout(3)
    17 
    18 while True:
    19   print("Waiting for connect...")
    20   try:
    21     connfd,addr = sockfd.accept()
    22   except (BlockingIOError,timeout) as e:
    23     # 如果没有客户端连接,每隔3秒写一个日志
    24     f.write("%s : %s
    "%(ctime(),e))
    25     f.flush()
    26     sleep(3)
    27   else:
    28     print("Connect from",addr)
    29     data = connfd.recv(1024).decode()
    30     print(data)
    31 
    32 # Waiting for connect...
    33 # Waiting for connect...
    34 # Waiting for connect...
    35 #log.txt文件逐渐写入
    36 # Thu Jun 27 00:29:29 2019 : timed out
    37 # Thu Jun 27 00:29:35 2019 : timed out
    38 # Thu Jun 27 00:29:41 2019 : timed out
    非阻塞IO示例

    IO多路复用

    1.定义:同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。以此形成可以同时处理多个IO的行为,避免一个IO阻塞造成其他IO均无法执行,提高了IO执行效率。

    2.具体方案:

    • select方法 : windows linux unix
    • poll方法: linux unix
    • epoll方法: linux

    select 方法

    rs, ws, xs=select(rlist, wlist, xlist[, timeout])

    • 功能: 监控IO事件,阻塞等待IO发生
    • 参数:
      • rlist 列表 存放关注的等待发生的IO事件
      • wlist 列表 存放关注的要主动处理的IO事件
      • xlist 列表 存放关注的出现异常要处理的IO
      • timeout 超时时间
    • 返回值:
      • rs 列表 rlist中准备就绪的IO
      • ws 列表 wlist中准备就绪的IO
      • xs 列表 xlist中准备就绪的IO

    select 实现tcp服务

    1.将关注的IO放入对应的监控类别列表

    2.通过select函数进行监控

    3.遍历select返回值列表,确定就绪IO事件

    4.处理发生的IO事件

    注意:

    wlist中如果存在IO事件,则select立即返回给ws

    处理IO过程中不要出现死循环占有服务端的情况

    IO多路复用消耗资源较少,效率较高

     1 """
     2 重点代码
     3 
     4 思路分析:
     5 1.将关注的IO放入对应的监控类别列表
     6 2.通过select函数进行监控
     7 3.遍历select返回值列表,确定就绪IO事件
     8 4.处理发生的IO事件
     9 """
    10 
    11 from socket import *
    12 from select import select
    13 
    14 # 创建一个监听套接字作为关注的IO
    15 s = socket()
    16 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    17 s.bind(('0.0.0.0',8888))
    18 s.listen(3)
    19 
    20 # 设置关注列表
    21 rlist = [s]
    22 wlist = []
    23 xlist = [s]
    24 
    25 # 循环监控IO
    26 while True:
    27   rs,ws,xs = select(rlist,wlist,xlist)
    28   # 遍历三个返回列表,处理IO
    29   for r in rs:
    30     # 根据遍历到IO的不同使用if分情况处理
    31     if r is s:
    32       c,addr = r.accept()
    33       print("Connect from",addr)
    34       rlist.append(c) # 增加新的IO事件
    35     # else为客户端套接字就绪情况
    36     else:
    37       data = r.recv(1024)
    38       # 客户端退出
    39       if not data:
    40         rlist.remove(r) # 从关注列表移除
    41         r.close()
    42         continue # 继续处理其他就绪IO
    43       print("Receive:",data.decode())
    44       # r.send(b'OK')
    45       # 我们希望主动处理这个IO对象
    46       wlist.append(r)
    47 
    48   for w in ws:
    49     w.send(b'OK')
    50     wlist.remove(w) # 使用后移除
    51 
    52   for x in xs:
    53     pass
    select tcp服务模型

    位运算

    定义 : 将整数转换为二进制,按二进制位进行运算

    运算符号:

    • & 按位与
    • | 按位或
    • ^ 按位异或
    • << 左移
    • >> 右移
    • 14 --> 01110
    • 19 --> 10011
    • 14 & 19 = 00010 = 2 一0则0
    • 14 | 19 = 11111 = 31 一1则1
    • 14 ^ 19 = 11101 = 29 相同为0不同为1
    • 14 << 2 = 111000 = 56 向左移动低位补0
    • 14 >> 2 = 11 = 3 向右移动去掉低位

    poll方法

    p = select.poll()

    • 功能 : 创建poll对象
    • 返回值: poll对象

    p.register(fd,event)   

    • 功能: 注册关注的IO事件
    • 参数:fd 要关注的IO

    event 要关注的IO事件类型

    常用类型:

    • POLLIN 读IO事件(rlist)
    • POLLOUT 写IO事件 (wlist)
    • POLLERR 异常IO (xlist)
    • POLLHUP 断开连接 

      p.register(sockfd,POLLIN|POLLERR)

    p.unregister(fd)

    • 功能:取消对IO的关注
    • 参数:IO对象或者IO对象的fileno

    events = p.poll()

    • 功能: 阻塞等待监控的IO事件发生
    • 返回值: 返回发生的IO

    events格式 [(fileno,event),()....]

    每个元组为一个就绪IO,元组第一项是该IO的fileno,第二项为该IO就绪的事件类型

    poll_server 步骤:

    1. 创建套接字
    2. 将套接字register
    3. 创建查找字典,并维护
    4. 循环监控IO发生
    5. 处理发生的IO
     1 """
     2 尽量掌握
     3 
     4 思路分析:
     5 1. 创建套接字作为监控IO
     6 2. 将套接字register
     7 3. 创建查找字典,并维护(要时刻与注册IO保持一致)
     8 4. 循环监控IO发生
     9 5. 处理发生的IO
    10 """
    11 
    12 from socket import *
    13 from select import *
    14 
    15 # 创建套接字
    16 s = socket()
    17 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    18 s.bind(('0.0.0.0',8888))
    19 s.listen(3)
    20 
    21 # 创建poll对象关注s
    22 p = poll()
    23 
    24 # 建立查找字典,用于通过fileno查找IO对象
    25 fdmap = {s.fileno():s}
    26 
    27 # 关注s
    28 p.register(s,POLLIN|POLLERR)
    29 
    30 # 循环监控
    31 while True:
    32   events = p.poll()
    33   # 循环遍历发生的事件 fd-->fileno
    34   for fd,event in events:
    35     # 区分事件进行处理
    36     if fd == s.fileno():
    37       c,addr = fdmap[fd].accept()
    38       print("Connect from",addr)
    39       # 添加新的关注IO
    40       p.register(c,POLLIN|POLLERR)
    41       fdmap[c.fileno()] = c # 维护字典
    42     # 按位与判定是POLLIN就绪
    43     elif event & POLLIN:
    44       data = fdmap[fd].recv(1024)
    45       if not data:
    46         p.unregister(fd) # 取消关注
    47         fdmap[fd].close()
    48         del fdmap[fd]  # 从字典中删除
    49         continue
    50       print("Receive:",data.decode())
    51       fdmap[fd].send(b'OK')
    poll 服务端程序

    epoll方法

    1.使用方法 : 基本与poll相同

    • 生成对象改为 epoll()
    • 将所有事件类型改为EPOLL类型

    2.epoll特点:

    • epoll 效率比select poll要高
    • epoll 监控IO数量比select要多
    • epoll 的触发方式比poll要多 (EPOLLET边缘触发)
     1 """
     2 尽量掌握
     3 """
     4 
     5 from socket import *
     6 from select import *
     7 
     8 # 创建套接字
     9 s = socket()
    10 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    11 s.bind(('0.0.0.0',8888))
    12 s.listen(3)
    13 
    14 # 创建epoll对象关注s
    15 ep = epoll()
    16 
    17 # 建立查找字典,用于通过fileno查找IO对象
    18 fdmap = {s.fileno():s}
    19 
    20 # 关注s
    21 ep.register(s,EPOLLIN|EPOLLERR)
    22 
    23 # 循环监控
    24 while True:
    25   events = ep.poll()
    26   # 循环遍历发生的事件 fd-->fileno
    27   for fd,event in events:
    28     print("亲,你有IO需要处理哦")
    29     # 区分事件进行处理
    30     if fd == s.fileno():
    31       c,addr = fdmap[fd].accept()
    32       print("Connect from",addr)
    33       # 添加新的关注IO
    34       # 将触发方式变为边缘触发
    35       ep.register(c,EPOLLIN|EPOLLERR|EPOLLET)
    36       fdmap[c.fileno()] = c # 维护字典
    37     # 按位与判定是EPOLLIN就绪
    38     # elif event & EPOLLIN:
    39     #   data = fdmap[fd].recv(1024)
    40     #   if not data:
    41     #     ep.unregister(fd) # 取消关注
    42     #     fdmap[fd].close()
    43     #     del fdmap[fd]  # 从字典中删除
    44     #     continue
    45     #   print("Receive:",data.decode())
    46     #   fdmap[fd].send(b'OK')
    epoll 服务端程序
  • 相关阅读:
    Websphere 系列的https证书的配置说明
    Linux下运行windows程序
    Linux常见命令(三)
    使用Java语言开发微信公众平台(八)——自定义菜单功能
    Linux常见命令(二)
    微信小程序,前端大梦想(八)
    微信小程序,前端大梦想(七)
    使用Java语言开发微信公众平台(七)——音乐消息的回复
    微信小程序,前端大梦想(六)
    微信小程序,前端大梦想(五)
  • 原文地址:https://www.cnblogs.com/maplethefox/p/11055941.html
Copyright © 2011-2022 走看看