深入解析golang?bufio
bufio 包介紹
bufio包實(shí)現(xiàn)了有緩沖的I/O。它包裝一個(gè)io.Reader或io.Writer接口對(duì)象,創(chuàng)建另一個(gè)也實(shí)現(xiàn)了該接口,且同時(shí)還提供了緩沖和一些文本I/O的幫助函數(shù)的對(duì)象。
golang bufio
當(dāng)頻繁地對(duì)少量數(shù)據(jù)讀寫時(shí)會(huì)占用IO,造成性能問題。golang的bufio
庫使用緩存來一次性進(jìn)行大塊數(shù)據(jù)的讀寫,以此降低IO系統(tǒng)調(diào)用,提升性能。
在Transport中可以設(shè)置一個(gè)名為WriteBufferSize
的參數(shù),該參數(shù)指定了底層(Transport.dialConn
)寫buffer的大小。
tr := &http.Transport{ WriteBufferSize: 64 * 1024, }
pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize()) pconn.bw = bufio.NewWriterSize(persistConnWriter{pconn}, t.writeBufferSize())
使用bufio進(jìn)行寫
可以使用bufio.NewWriter
初始化一個(gè)大小為4096字節(jié)的Writer
(見下),或使用bufio.NewWriterSize
初始化一個(gè)指定大小的Writer
。
Writer
中的主要參數(shù)為緩存區(qū)buf
,緩存區(qū)中的數(shù)據(jù)偏移量n
以及寫入接口wr
:
type Writer struct { err error buf []byte n int wr io.Writer }
bufio.Writer
方法可以一次性寫入緩存中的數(shù)據(jù),通常有如下三種情況:
- 緩存中滿數(shù)據(jù)
- 緩存中仍有空間
- 待寫入的數(shù)據(jù)大于緩存的大小
緩存中滿數(shù)據(jù)
當(dāng)緩存中滿數(shù)據(jù)時(shí),會(huì)執(zhí)行寫操作。
緩存中仍有空間
如果緩存中仍有數(shù)據(jù),則不會(huì)執(zhí)行寫入動(dòng)作,除非調(diào)用Flush()
方法。
待寫入的數(shù)據(jù)大于緩存的大小
由于此時(shí)緩存無法緩存足夠的數(shù)據(jù),此時(shí)會(huì)跳過緩存直接執(zhí)行寫操作
type Writer int func (*Writer) Write(p []byte) (n int, err error) { fmt.Printf("Writing: %s\n", p) return len(p), nil } func main() { w := new(Writer) bw1 := bufio.NewWriterSize(w, 4) // Case 1: Writing to buffer until full bw1.Write([]byte{'1'}) bw1.Write([]byte{'2'}) bw1.Write([]byte{'3'}) bw1.Write([]byte{'4'}) // write - buffer is full // Case 2: Buffer has space bw1.Write([]byte{'5'}) //此時(shí)buffer中無法容納更多的數(shù)據(jù),執(zhí)行寫操作,寫入 []byte{'1','2','3','4'} err = bw1.Flush() // forcefully write remaining if err != nil { panic(err) } // Case 3: (too) large write for buffer // Will skip buffer and write directly bw1.Write([]byte("12345")) //buffer不足,直接執(zhí)行寫操作 //結(jié)果: Writing: 1234 Writing: 5 Writing: 12345
緩存重用
申請(qǐng)緩存對(duì)性能是有損耗的,可以使用Reset
方法重置緩存,其內(nèi)部只是將Writer
的數(shù)據(jù)偏移量n
置0。
wr := new(Writer) bw := bufio.NewWriterSize(wr,2) bw.Reset(wr)
獲取緩存的可用空間數(shù)
Available()
方法可以返回緩存的可用空間數(shù),即len(Writer.buf)-Writer.n
使用bufio進(jìn)行讀
與用于寫數(shù)據(jù)的Writer
類似,讀數(shù)據(jù)也有一個(gè)Reader
,可以使用NewReader
初始化一個(gè)大小為4096字節(jié)的Reader
,或使用NewReaderSize
初始化一個(gè)指定大小的Reader
(要求最小為16字節(jié))。Reader
也有一個(gè)記錄偏移量的變量r
type Reader struct { buf []byte rd io.Reader // reader provided by the client r, w int // buf read and write positions err error lastByte int // last byte read for UnreadByte; -1 means invalid lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid }
Peek
該方法會(huì)返回buf中的前n個(gè)字節(jié)的內(nèi)容,但與Read操作不同的是,它不會(huì)消費(fèi)緩存中的數(shù)據(jù),即不會(huì)增加數(shù)據(jù)偏移量,因此通常也會(huì)用于判斷是否讀取結(jié)束(EOF)。通常有如下幾種情況:
- 如果peak的值小于緩存大小,則返回相應(yīng)的內(nèi)容
- 如果peak的值大于緩存大小,則返回bufio.ErrBufferFull錯(cuò)誤
- 如果peak的值包含EOF且小于緩存大小,則返回EOF
Read
將數(shù)據(jù)讀取到p
,涉及將數(shù)據(jù)從緩存拷貝到p
。
func (b *Reader) Read(p []byte) (n int, err error)
ReadSlice
該方法會(huì)讀從緩存讀取數(shù)據(jù),直到遇到第一個(gè)delim
。如果緩存中沒有delim
,則返回EOF,如果查詢的長(zhǎng)度超過了緩存大小,則返回 io.ErrBufferFull
錯(cuò)誤。
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
例如delim
為','
,則下面會(huì)返回的內(nèi)容為1234,
。
r := strings.NewReader("1234,567") rb := bufio.NewReaderSize(r, 20) fmt.Println(rb.ReadSlice(',')) // 結(jié)果:[49 50 51 52 44] <nil>
注意:
ReadSlice
返回的是原始緩存中的內(nèi)容,如果針對(duì)緩存作并發(fā)操作,則返回的內(nèi)容有可能被其他操作覆蓋。因此在官方注釋里面有寫,建議使用ReadBytes
或ReadString
。但ReadBytes
和ReadString
涉及內(nèi)存申請(qǐng)和拷貝,因此會(huì)影響性能。在追求高性能的場(chǎng)景下,建議外部使用sync.pool
來提供緩存。// Because the data returned from ReadSlice will be overwritten // by the next I/O operation, most clients should use // ReadBytes or ReadString instead.
ReadLine
ReadLine() (line []byte, isPrefix bool, err error)
ReadLine
底層用到了ReadSlice
,但在返回時(shí)會(huì)移除\n
或\r\n
。需要注意的是,如果切片中沒有找到換行符,則不會(huì)返回EOF或io.ErrBufferFull
錯(cuò)誤,相反,它會(huì)將isPrefix
置為true
ReadBytes
與ReadSlice
類似,但它會(huì)返回一個(gè)新的切片,因此便于并發(fā)使用。如果找不到delim
,ReadBytes
會(huì)返回io.EOF
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
Scanner
scanner可以不斷將數(shù)據(jù)讀取到緩存(默認(rèn)64*1024字節(jié))。
rb := strings.NewReader("12345678901234567890") scanner := bufio.NewScanner(rb) for scanner.Scan() { fmt.Printf("Token (Scanner): %q\n", scanner.Text()) } // 結(jié)果:Token (Scanner): "12345678901234567890"
參考
how-to-read-and-write-with-golang-bufio
到此這篇關(guān)于golang bufio解析的文章就介紹到這了,更多相關(guān)golang bufio內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作
這篇文章主要介紹了Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12Go錯(cuò)誤和異常CGO?fallthrough處理教程詳解
這篇文章主要為大家介紹了Go錯(cuò)誤和異常CGO?fallthrough使用教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Go語言reflect.TypeOf()和reflect.Type通過反射獲取類型信息
這篇文章主要介紹了Go語言reflect.TypeOf()和reflect.Type通過反射獲取類型信息,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04golang實(shí)現(xiàn)瀏覽器導(dǎo)出excel文件功能
這篇文章主要介紹了golang實(shí)現(xiàn)瀏覽器導(dǎo)出excel文件功能,文章通過golang導(dǎo)出excel文件返回給web,實(shí)現(xiàn)瀏覽器導(dǎo)出excel文件功能,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-03-03Go語言使用net/http實(shí)現(xiàn)簡(jiǎn)單登錄驗(yàn)證和文件上傳功能
這篇文章主要介紹了Go語言使用net/http實(shí)現(xiàn)簡(jiǎn)單登錄驗(yàn)證和文件上傳功能,使用net/http模塊編寫了一個(gè)簡(jiǎn)單的登錄驗(yàn)證和文件上傳的功能,在此做個(gè)簡(jiǎn)單記錄,需要的朋友可以參考下2023-07-07深入了解Go的interface{}底層原理實(shí)現(xiàn)
本文主要介紹了Go的interface{}底層原理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06golang網(wǎng)絡(luò)socket粘包問題的解決方法
這篇文章主要介紹了golang網(wǎng)絡(luò)socket粘包問題的解決方法,簡(jiǎn)單講述了socket粘包的定義并結(jié)合實(shí)例形式分析了Go語言解決粘包問題的方法,需要的朋友可以參考下2016-07-07Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說明
這篇文章主要介紹了Golang中基礎(chǔ)的命令行模塊urfave/cli的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12