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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM