Python中同步与异步编程

1)同步、异步

函数或方法被调用的时候,调用者是否得到最终的结果。

直接得到最终结果的结果,就是同步调用。(打饭模型,打饭不打好不走开,直到打饭给我后才离开)

不直接得到的最终的结果,就是异步调用。(打饭,不会一直等着,会时不时的过来看看,打完了把饭拿走,异步不保证多长时间打完了饭)

 

2)阻塞、非阻塞:

函数或方法调用的时候,是否立即返回。

立即返回就是非阻塞调用。

不立即返回就是阻塞调用。

 

3)区别:

同步、异步,与阻塞、非阻塞不相关。

同步、异步强调的是结果。

阻塞和非阻塞强调的是时间,是否等待。

 

 

同步与异步区别在于:调用者是否得到了想要的最终结果。

同步就是一直要执行到返回最终结果。

异步就是直接返回了,但是返回的不是最终的结果,调用者不能通过这种调用得到结果,还要通过被调用者,使用其他方式通知调用者,来取回最终结果。

 

阻塞与非阻塞的区别在于,调用者是否还能干其他的事情。

阻塞,调用者只能干等。

非阻塞,调用者可以先忙一会别的,不用一直等。

 

4)联系:

同步阻塞:调用者阻塞,直到等到拿到最终结果。(打饭模型,什么事情也不敢,就等着打饭,打饭是结果,什么也不干,一直在等着,同步加阻塞)

同步非阻塞:(等着打饭,但是可以玩会手机,看看电视,打饭是结果,但是不用一直在等着)

 

异步阻塞:(我要打饭,你说等着较好,并没有返回饭给我,我啥事不干,就干等着饭好了叫我)

异步非阻塞:回调的话。(我要打饭,你说等较好,并没有返回饭给我,可以在旁边看看电视,玩玩手机,饭好了叫我)

 

5)同步IO、异步IO、IO多路复用

IO模型:

IO分为两个阶段。

1)数据准备阶段。

2)内核空间复制回用户进程缓冲区阶段。

发生IO的时候:

1、内核从输入设备读、写数据(淘米,把米放锅里煮饭)

2、进程从内核复制数据(盛饭,从内核这个锅把饭装到碗里面来)

 

系统调用  -- read函数

 

从磁盘读取到内核空间中来,在拷贝到用户的应用空间内。

 

系统调用read函数。内核空间,用户空间。

5)IO模型

IO模型:同步IO,包括阻塞IO,非阻塞IO,IO多路复用。

阻塞IO

 

进程等待(阻塞),直到读写完成。(全程等待) read/write函数

 

进程调用read操作,如果IO设备没有准备好,立即返回error,进程不阻塞,用户可以再次 发起系统调用,如果内核已经准备好了,就阻塞,然后复制数据到用户空间。

 

第一阶段数据没有准备好,就先忙别的,等来再来看看,检查数据是否准备好了的过程是非阻塞的。

第二阶段是阻塞的,即内核空间和用户空间之间复制数据是阻塞的。

淘米、蒸饭不用等,去完后。盛饭过程等着你装好饭,但是要等到盛好饭才算完事,这个是同步的,结果就是盛好饭。

Read/write

 

 

IO多路复用:

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

Select几乎所有操作系统平台都支持,poll是对select的升级。

epoll,Linux系统内核2.5+开始的。对select和poll的增强,在监视的基础上,增加回调机制,BSD。Mac平台都有kqueue,window是有iocp

 

 

回调:

Select为例,将关注的IO操作告诉select函数并调用,进程阻塞,内核监视select关注的文件描述符fd,被关注的任何一个fd对应的IO准备好了数据,select返回,在使用read将数据复制到用户进程。

 

 

Select:最多监听1024个fd。IO多了,每次都要遍历fd全部发送,效率低下。

epoll:通知机制。没有fd的上限,且是回调机制。效率很高。

 

 

 

6)异步IO:

 

 

异步的性能是非常高的。

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

举例:今天不想出去吃饭了,点外卖,饭菜在饭店做好了(第一阶段),送餐员会从饭店送到家门口(第二阶段)

 

 

 

7)Python中IO多路复用:

IO多路复用:

大多数操作系统支持select和poll

Linux2.5+支持epoll

BSD、mac支持kqueue。

Windows的IOcp

 

Python中的select库。

实现了select,poll系统调用,这个基本上操作系统都支持,部分实现了poll,底层的IO多路复用模块。

 

开发中的选择

1)完全跨平台,使用select,poll,但是性能较差。

2)针对不同操作系统自行选择支持的技术,这样做会提高IO处理的性能。

 

 

要访问的IO交给select,由其注册,

 

selectors库:

3.4版本提供的库,高级IO复用库。

BaseSelector

+-- SelectSelector

+-- PollSelector

+-- EpollSelector

+-- DevpollSelector

+-- KqueueSelector

 

Selectors.Defaultselector返回当前平台最有效、效能最高的实现。

但是没有实现windows下的iocp,所以,只能退化为select。

 

Abstractmethod register(fileobj,events,data=None)

为selector注册一个文件对象,监视他的IO事件。

Fileobj被监视文件对象,例如socket对象。

Events事件,该文件对象必须等待的时间。

Data 可选的与此文件对象相关联的不透明数据,例如,关联用户存储每个客户端的会话ID,关联方法,通过参数在关注的事件产生后让selector干什么事。

 

Event常量

含义

Event_read

可读0b01,内核已经准备好输入输出设备,可以开始读了

Event_write

可写0b10,内核准备好了,可以往里面写了。

import selectors
import threading
import socket
import logging


FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)

def accept(sock:socket.socket,mask):
    conn,raddr = sock.accept()
    conn.setblocking(False)
    key = selector.register(conn,selectors.EVENT_READ,read)
    logging.info(key)


def read(conn:socket.socket,mask):
    data = conn.recv(1024)
    msg = 'msg is {]'.format(data.decode())
    conn.send(msg.encode())

selector = selectors.DefaultSelector()

sock = socket.socket()
sock.bind(('127.0.0.1',8080))
sock.listen()
logging.info(sock)

sock.setblocking(False)

key = selector.register(sock,selectors.EVENT_READ,accept)
logging.info(key)

e = threading.Event()

def select(e):
    while not e.is_set():
        events = selector.select()
        print('========')
        for key,mask in events:
            logging.info(key)
            logging.info(mask)
            callback = key.data
            callback(key.fileobj,mask)

threading.Thread(target=select,args=(e,),name='select').start()


def main():
    while not e.is_set():
        cmd = input('>>>')
        if cmd.strip() == 'quit':
            e.set()
            fobjs = []
            logging.info('{}'.format(list(selector.get_map().items())))

            for fd ,key in selector.get_map().items():
                print(fd)
                print(key)
                fobjs.append(key.fileobj)

            for fobj in fobjs:
                selector.unregister(fobj)
                fobj.close()
            selector.close()

if __name__ == '__main__':
    main()
posted @ 2018-11-12 23:30  Python爱好者666  阅读(7398)  评论(0编辑  收藏  举报