同步异步、阻塞与非阻塞、IO

一、同步与异步,阻塞与非阻塞

  1、同步与异步

  同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)。同步,程序调用某个东西时,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反 调用方不会立即得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用

  同步:进程之间存在依赖关系,一个进程结束的输出作为另一个进程的输入。具有同步关系的一组并发进程之间发送的信息称为消息或者事件;

  异步:和同步相对,同步是顺序执行,而异步是彼此独立,在等待某个事件的过程中继续做自己的事,不要等待这一事件完成后再工作。线程是实现异步的一个方式,异步是让调用方法的主线程不需要同步等待另一个线程的完成,从而让主线程做其他事情。

  

  2、阻塞与非阻塞

  阻塞和非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。阻塞时,在调用结果返回前,当前线程会被挂起,并在得到结果之后返回。非阻塞时,如果不能立刻得到结果,则该调用者不会阻塞当前线程。因此对应非阻塞的情况,调用者需要定时轮询查看处理状态。

 

  3、两者区别与联系

区别:

同步、异步强调的是否得到最终的结果
阻塞、非阻塞强调的时间,是否等待

同步与异步区别在于:调用者是否得到了想要的结果。
同步就是一直要执行到返回最终结果
异步就是直接返回了,但是返回的不是最终结果。调用者不能通过这种调用得到结果,还是通过被调用者,使用其它方式通知调用者,来取回最终结果

阻塞与非阻塞的区别在于,调用者是否还能干其他事
阻塞,调用者就一直挂起,一直等着程序
非阻塞,调用者可以先去进行其他工作,不用一直挂起

联系:

同步阻塞:一直挂起,什么都不用做,只需要等结果

同步非阻塞:等着结果,但是不一定等,可以干其他事情

异步阻塞:我要调用某个东西,但是你却没有,返回一个没有东西的结果,然后我就一直等着啥也不干。等着结果返回通知

异步非阻塞:我要调用某个东西,但是你却没有,返回一个没有东西的结果,然后我可以干其他事情。等着结果返回后通知

 

二、IO模型 

  对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

      数据准备阶段

      内核空间负责回用户进程缓冲区阶段

    发生IO的时候:

      内核从输入设备读、写数据

      进程从内核复制数据

 

  1、同步IO模型包括:阻塞IO、非阻塞IO、IO多路复用

 

    阻塞Io:进程一直等待,直到读写完成

    非阻塞IO

      如果IO没有准备好,立即返回ERROR,进程不阻塞。再进行下一个轮询,用户可以再次发起系统调用,如果内核已经准备好,就阻塞,然后复制数据到用户空间,

 

IO多路复用:

同时监控多个IO,有一个准备好了,就不需要在等了开始处理,提高了同时处理IO的能力  

 

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

select:

  1、服务端

import select,socket,queue
server = socket.socket()
server.bind(("localhost",9000))
server.listen(1000)
server.setblocking(False)      #设置为非阻塞
msg_dic = dict()        #定义一个队列字典
inputs = [server,]  #由于设置成非阻塞模式,accept和recive都不阻塞了,没有值就会报错,因此最开始需要最开始需要监控服务端本身,等待客户端连接
outputs = [] 
while True:
    #exceptional表示如果inputs列表中出现异常,会输出到这个exceptional中
    readable,writeable,exceptional = select.select(inputs,outputs,inputs)#如果没有任何客户端连接,就会阻塞在这里 
    for r in readable:# 没有个r代表一个socket链接
        if r is server:  #如果这个socket是server的话,就说明是是新客户端连接了
            conn,addr = r.accept() #新连接进来了,接受这个连接,生成这个客户端实例
            print("来了一个新连接",addr)
            inputs.append(conn)#为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
            #就会被交给select去监听
            msg_dic[conn] = queue.Queue() #初始化一个队列,后面存要返回给这个客户端的数据
        else: #如果不是server,就说明是之前建立的客户端来数据了
            data = r.recv(1024)
            print("收到数据:",data)
            msg_dic[r].put(data)#收到的数据先放到queue里,一会返回给客户端
            outputs.append(r)#为了不影响处理与其它客户端的连接 , 这里不立刻返回数据给客户端
            # r.send(data)
            # print("send done....")
    for w in writeable:  #要返回给客户端的链接列表
        data_to_client = msg_dic[w].get()
        w.send(data_to_client)   #返回给客户端的源数据
        outputs.remove(w)  #确保下次循环的时候writeable,不返回这个已经处理完的这个连接了
 
    for e in exceptional:  #处理异常的连接
        if e in outputs:   #因为e不一定在outputs,所以先要判断
            outputs.remove(e)
        inputs.remove(e)   #删除inputs中异常连接
        del msg_dic[e]   #删除此连接对应的队列

 

 

 

 

 

 

    异步IO

      进程发起异步IO请求,立即返回。内核完成IO的阶段的两个阶段,内核给进程发一个信号。

 

posted @ 2022-03-31 16:29  新入世界的小白  阅读(77)  评论(0)    收藏  举报