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.