io重要的介面
在介紹buffer之前,先來認識兩個重要的介面,如下邊所示:
type Reader interface { Read(p []byte) (n int, err error)}type Writer interface { Write(p []byte) (n int, err error)}
上邊兩個介面在golang sdk安裝目錄src/io/io.go中定義。後邊凡是涉及到io相關操作的,基本上都實現了這兩個介面,如:
1. package bufio 中的Reader類2. package bytes 中的Reader類與Buffer類3. package os 中 的File類,這個實現的最為複雜,主要由於在檔案操作中,需要系統底層提供服務。...不再一一列舉...
經常聽說有這麼一句話:“使用I/O buffer,有助於提高效率”。但是,我想反問的是,真的提高了效率了嗎?
buffer在什麼情況下會提高我們的程式效能呢?帶著這個問題,我們來剖析一下上邊提到的幾個類。
1.第一個類bytes.Reader
這個類,實現了io.Reader介面,但是這個類沒有實現io.Writer介面。這個類沒有buffer,為啥?因為這個類,在初始化時,將字元流傳入到對象中儲存,沒有提供Write方法寫入新的字元流。所以,這個類不需要buffer。
2.第二個類bytes.Buffer
這個類實現了io.Reader與io.Writer介面,這個類在寫入位元組流的過程中,使用到了buffer,怎麼實現的呢?
在初始化這個類時,會傳入一個[]byte類型的slice到對象中,當Write方法向這個對象中寫入位元組流時,如果之前傳入的這個[]byte申請的空間不夠時,Write會調用自身的Grow方法,給這個[]byte類型的slice類型擴容,這樣,這個裡邊的buffer會隨著寫入量增大,長度會不斷的擴大。如果此處沒有buffer的話,當寫入容量滿時,要麼阻塞,要麼迴圈寫入,這樣會導致系統卡死或資料被破壞,當引入buffer後,解決了上邊的兩個問題。但是這種解決方式,存在一個隱患,也就是如果出現讀取死迴圈,這樣會造成記憶體溢出。
3.第三個類bufio.Reader
這個類實現了io.Reader介面,這個類在執行個體化時,需要傳入一個io.Reader類型的變數,這問題就來了,一個io.Reader類型的變數,一定是實現了Read方法了,那麼為什麼還需要裝進bufio.Reader對象中呢?原來,bufio.Reader類中得Read方法,在讀取位元組流時,對傳入的[]byte類型變數空間長度進行檢查,如果傳入變數的長度小於bufio.Reader初始化的容量,將會首先調用io.Reader自己的Read方法,將內容寫入到bufio.Reader對象的buffer中,然後將值複製給傳入的[]byte變數。這樣做的好處是,在執行io.Reader的Read方法時,多讀取一些位元組,這樣對於像檔案操作就大有裨益。
4.第四個類os.File
這個類實現了io.Writer與io.Reader類,但是有點特殊的是,os.File的Read方法與Write均需要藉助於系統層面的檔案操作方法.總所周知,在檔案讀取時,Read與Write方法時沒有緩衝的,也就是你讀幾個位元組,取決於你傳入的變數容量是多少,如果容量為1,那麼對於檔案讀取而言,就會很慢,所以將os.File的對象,傳入到bufio.Reader對象中,這樣可以在某些程度上提高效率,哪些時候呢?就是你在調用Read方法時,傳入的變數容量太小時,會提高讀取效率.但是bufio.Reader提供的Read方法不能保證每次讀到的字元數一致,這個與其實現方式有關,但是不影響我們使用,只要確保收到EOF,再停止讀取即可.
總結
在使用I/O操作時,bufio包提供了帶buffer的方式讀取I/O流,在操作檔案讀取,報文讀取等上,可以在某種程度上提高效率,bufio中的類,並沒有從底層實現Read與Write方法,只是限定了最小讀取量.這個最小量就是bufio.Reader初始化長度.
bytes.Buffer提供的buffer十分強大,這個類不僅實現了io.Reader介面,還實現了io.Writer介面.所以bytes.Buffer的對象不僅可以讀取,還可以追加寫入,寫入的過程中,容量還可以自動擴充,所以,功能十分強大.但是在使用時,要注意安全,bytes.Buffer會不斷的擴大,擴大,最終還會panic.