淺析Go語(yǔ)言中的緩沖區(qū)及其在fmt包中的應(yīng)用
傳統(tǒng)的IO流程
在傳統(tǒng)的IO流程中,通常涉及以下幾個(gè)步驟:
- 打開文件或建立網(wǎng)絡(luò)連接:首先,需要打開文件或建立網(wǎng)絡(luò)連接,以便進(jìn)行讀取或?qū)懭氩僮?。這通常涉及到操作系統(tǒng)提供的系統(tǒng)調(diào)用,如
open
、socket
等。 - 讀取或?qū)懭霐?shù)據(jù):一旦文件或網(wǎng)絡(luò)連接打開,就可以進(jìn)行數(shù)據(jù)的讀取或?qū)懭氩僮?。讀取操作將從文件或網(wǎng)絡(luò)連接中獲取數(shù)據(jù),而寫入操作將將數(shù)據(jù)寫入文件或發(fā)送到網(wǎng)絡(luò)連接中。這些操作通常涉及到系統(tǒng)調(diào)用,如
read
和write
。 - 緩沖區(qū):為了提高IO性能,通常會(huì)使用緩沖區(qū)。緩沖區(qū)是一塊內(nèi)存區(qū)域,用于臨時(shí)存儲(chǔ)要讀取或?qū)懭氲臄?shù)據(jù)。數(shù)據(jù)首先被讀取到緩沖區(qū)中,然后從緩沖區(qū)中寫入到文件或網(wǎng)絡(luò)連接中,或者從緩沖區(qū)中讀取數(shù)據(jù)。
- 關(guān)閉文件或網(wǎng)絡(luò)連接:當(dāng)讀取或?qū)懭氩僮魍瓿珊?,需要關(guān)閉文件或網(wǎng)絡(luò)連接。這通常涉及到系統(tǒng)調(diào)用,如
close
。
在傳統(tǒng)的IO流程中,每次讀取或?qū)懭氩僮鞫紩?huì)涉及到系統(tǒng)調(diào)用,這會(huì)導(dǎo)致較高的開銷。為了提高性能,通常會(huì)使用緩沖區(qū)來減少系統(tǒng)調(diào)用的次數(shù)。緩沖區(qū)可以一次讀取或?qū)懭攵鄠€(gè)數(shù)據(jù),從而減少了系統(tǒng)調(diào)用的開銷。
緩沖區(qū)
上面我們了解到緩沖區(qū)這個(gè)概念,那什么是緩沖區(qū)呢?
內(nèi)存緩沖區(qū)是計(jì)算機(jī)中的一種臨時(shí)存儲(chǔ)區(qū)域,用于暫時(shí)存儲(chǔ)數(shù)據(jù)。它通常用于提高數(shù)據(jù)讀寫的效率,減少對(duì)底層存儲(chǔ)設(shè)備的頻繁訪問。
緩沖區(qū)的主要目的是在數(shù)據(jù)的生產(chǎn)者和消費(fèi)者之間起到一個(gè)中間層的作用。當(dāng)數(shù)據(jù)被生產(chǎn)者生成時(shí),它首先被寫入緩沖區(qū),而不是直接寫入到目標(biāo)存儲(chǔ)設(shè)備。然后,消費(fèi)者可以從緩沖區(qū)中讀取數(shù)據(jù)。
緩沖區(qū)的大小是有限的,一旦緩沖區(qū)被填滿,生產(chǎn)者必須等待消費(fèi)者讀取數(shù)據(jù),以便為新的數(shù)據(jù)騰出空間。同樣,如果緩沖區(qū)為空,消費(fèi)者必須等待生產(chǎn)者生成新的數(shù)據(jù)。
內(nèi)存緩沖區(qū)可以用于各種場(chǎng)景,比如:
- 文件讀寫:在讀取或?qū)懭氪笪募r(shí),可以使用內(nèi)存緩沖區(qū)來提高讀寫性能。數(shù)據(jù)首先被讀取到緩沖區(qū)中,然后批量地寫入或讀取到磁盤上的文件。
- 網(wǎng)絡(luò)通信:在網(wǎng)絡(luò)通信中,數(shù)據(jù)通常需要經(jīng)過網(wǎng)絡(luò)協(xié)議的封裝和解析。使用內(nèi)存緩沖區(qū)可以將數(shù)據(jù)暫時(shí)存儲(chǔ)起來,以便進(jìn)行協(xié)議處理和網(wǎng)絡(luò)傳輸。
- 數(shù)據(jù)庫(kù)操作:在數(shù)據(jù)庫(kù)操作中,使用內(nèi)存緩沖區(qū)可以提高數(shù)據(jù)的讀寫性能。數(shù)據(jù)首先被寫入緩沖區(qū),然后批量地寫入到數(shù)據(jù)庫(kù)中,或者從緩沖區(qū)中讀取數(shù)據(jù)進(jìn)行查詢。
需要注意的是,內(nèi)存緩沖區(qū)只是一個(gè)臨時(shí)存儲(chǔ)區(qū)域,數(shù)據(jù)在緩沖區(qū)中并不是持久化的。一旦程序結(jié)束或緩沖區(qū)被清空,緩沖區(qū)中的數(shù)據(jù)就會(huì)丟失。因此,在使用內(nèi)存緩沖區(qū)時(shí)需要確保數(shù)據(jù)的正確性和一致性。
go緩沖區(qū)
在Go語(yǔ)言中,緩沖區(qū)的大小是由創(chuàng)建緩沖區(qū)時(shí)指定的參數(shù)決定的。在標(biāo)準(zhǔn)庫(kù)中,可以使用bufio
包提供的NewWriterSize
函數(shù)創(chuàng)建一個(gè)指定大小的緩沖區(qū)。
默認(rèn)情況下,bufio.Writer
的緩沖區(qū)大小為4096字節(jié)(4KB),即調(diào)用bufio.NewWriter
創(chuàng)建的緩沖區(qū)大小為4096字節(jié)。這是因?yàn)?096字節(jié)是一個(gè)常見的磁盤塊大小,對(duì)于大多數(shù)應(yīng)用場(chǎng)景來說,這個(gè)大小已經(jīng)足夠了。
如果需要自定義緩沖區(qū)的大小,可以使用bufio.NewWriterSize
函數(shù)來指定緩沖區(qū)的大小。例如,可以通過bufio.NewWriterSize(writer, 8192)
來創(chuàng)建一個(gè)大小為8192字節(jié)(8KB)的緩沖區(qū)。
為什么
為什么go編程中要設(shè)置緩沖區(qū)呢?其實(shí)我們上面都有提到:設(shè)置緩沖區(qū)的一個(gè)主要目的就是為了減少頻繁的IO操作。
在進(jìn)行IO操作時(shí),例如讀取或?qū)懭胛募看味贾苯硬僮鞯讓拥拇鎯?chǔ)設(shè)備(如磁盤或網(wǎng)絡(luò))可能會(huì)導(dǎo)致性能下降。這是因?yàn)槊看蜪O操作都需要進(jìn)行系統(tǒng)調(diào)用,這涉及到內(nèi)核和用戶空間之間的上下文切換,以及硬件設(shè)備的訪問延遲。
通過使用緩沖區(qū),可以將數(shù)據(jù)暫時(shí)存儲(chǔ)在內(nèi)存中,而不是直接與底層存儲(chǔ)設(shè)備進(jìn)行交互。這樣可以將多個(gè)小的IO操作合并為一個(gè)大的IO操作,從而減少了系統(tǒng)調(diào)用的次數(shù)。這種批量處理的方式通常比頻繁的小IO操作更高效。
此外,緩沖區(qū)還可以提供更好的數(shù)據(jù)傳輸效率。當(dāng)數(shù)據(jù)被寫入緩沖區(qū)時(shí),實(shí)際的IO操作可以被推遲到緩沖區(qū)被填滿或手動(dòng)刷新緩沖區(qū)時(shí)才執(zhí)行。這樣可以減少IO操作的次數(shù),提高數(shù)據(jù)傳輸?shù)男省?/p>
go 緩沖區(qū)(Buffer)是分配在堆還是棧?
在Go語(yǔ)言中,緩沖區(qū)(Buffer)的申請(qǐng)是在堆上進(jìn)行的。
Go語(yǔ)言中的??臻g是有限的,而且棧上的內(nèi)存分配和釋放是由編譯器自動(dòng)管理的,無法手動(dòng)控制。因此,較大的緩沖區(qū)無法放在棧上進(jìn)行申請(qǐng)。
相反,Go語(yǔ)言中的堆空間是用于動(dòng)態(tài)分配內(nèi)存的,可以手動(dòng)控制內(nèi)存的申請(qǐng)和釋放。當(dāng)我們使用make
關(guān)鍵字創(chuàng)建一個(gè)切片或映射時(shí),內(nèi)存就會(huì)在堆上進(jìn)行動(dòng)態(tài)分配。而bufio
包中的緩沖區(qū)也是通過make
函數(shù)在堆上進(jìn)行申請(qǐng)的。
緩沖區(qū)的申請(qǐng)通常是在創(chuàng)建緩沖區(qū)時(shí)進(jìn)行的,例如使用bufio.NewWriter
或bufio.NewWriterSize
函數(shù)來創(chuàng)建一個(gè)緩沖區(qū)對(duì)象。這個(gè)過程會(huì)調(diào)用make
函數(shù)來分配足夠大小的內(nèi)存,并返回一個(gè)指向該內(nèi)存的指針。
fmt打印
示例
fmt.Println("Hello, world!"),大家平時(shí)用得最多了,這不就是打印輸出到控制臺(tái)嘛
當(dāng)執(zhí)行fmt.Println("Hello, world!")
命令時(shí),會(huì)調(diào)用fmt
包內(nèi)的Println
函數(shù)來打印輸出。
首先,Println
函數(shù)會(huì)根據(jù)傳入的參數(shù)列表構(gòu)建一個(gè)字符串,并將其傳遞給Fprintln
函數(shù)。Fprintln
函數(shù)是fmt
包內(nèi)部的一個(gè)輔助函數(shù),它會(huì)將構(gòu)建的字符串寫入到標(biāo)準(zhǔn)輸出(即控制臺(tái))。
在Fprintln
函數(shù)內(nèi)部,它會(huì)調(diào)用newPrinter
函數(shù)來創(chuàng)建一個(gè)pp
(printer)對(duì)象。pp
對(duì)象是printer
結(jié)構(gòu)體的實(shí)例,它包含了打印輸出的相關(guān)配置和狀態(tài)信息。
接下來,Fprintln
函數(shù)會(huì)調(diào)用pp.print
方法來實(shí)際執(zhí)行打印輸出的操作。在print
方法中,它會(huì)根據(jù)配置的格式化選項(xiàng),將構(gòu)建的字符串寫入到pp.buf
緩沖區(qū)中。
如果緩沖區(qū)已滿,或者遇到換行符(\n
),print
方法會(huì)調(diào)用pp.write
方法將緩沖區(qū)的內(nèi)容寫入到標(biāo)準(zhǔn)輸出。write
方法會(huì)使用os.Stdout
作為目標(biāo),將緩沖區(qū)的內(nèi)容寫入到控制臺(tái)。
最后,Fprintln
函數(shù)會(huì)調(diào)用pp.free
方法來釋放pp
對(duì)象占用的內(nèi)存,以及清空緩沖區(qū)。
總結(jié)起來,當(dāng)執(zhí)行fmt.Println("Hello, world!")
命令時(shí),fmt
包內(nèi)部會(huì)構(gòu)建打印輸出的字符串,并將其寫入到標(biāo)準(zhǔn)輸出。這個(gè)過程涉及到字符串的構(gòu)建、緩沖區(qū)的管理和標(biāo)準(zhǔn)輸出的寫入。通過使用printer
結(jié)構(gòu)體和相關(guān)方法,fmt
包實(shí)現(xiàn)了方便的打印輸出功能。
源碼查看
// ... func main() { fmt.Println("Hello, world!") } // fmt 包 // ... func Println(a ...any) (n int, err error) { return Fprintln(os.Stdout, a...) } // ... func Fprintln(w io.Writer, a ...any) (n int, err error) { p := newPrinter() p.doPrintln(a) n, err = w.Write(p.buf) p.free() return }
當(dāng)打印內(nèi)容很大怎么辦
當(dāng)打印的內(nèi)容超出了緩沖區(qū)的大小時(shí),fmt
包會(huì)動(dòng)態(tài)擴(kuò)展緩沖區(qū)的大小,以容納更大的數(shù)據(jù),并完整地輸出到標(biāo)準(zhǔn)輸出。
在執(zhí)行打印操作時(shí),fmt
包會(huì)檢查緩沖區(qū)的剩余空間是否足夠容納當(dāng)前要打印的內(nèi)容。如果空間不足,fmt
包會(huì)自動(dòng)擴(kuò)展緩沖區(qū)的大小,通常是將緩沖區(qū)的大小翻倍。
例如,在默認(rèn)情況下,fmt
包的緩沖區(qū)大小為4KB。如果要打印的內(nèi)容超過了4KB,fmt
包會(huì)自動(dòng)將緩沖區(qū)擴(kuò)展到8KB,以容納更多的數(shù)據(jù)。如果仍然不夠,會(huì)繼續(xù)擴(kuò)展到16KB,以此類推,直到能夠容納所有的數(shù)據(jù)。
當(dāng)緩沖區(qū)大小足夠容納要打印的內(nèi)容時(shí),fmt
包會(huì)將數(shù)據(jù)寫入緩沖區(qū)中。當(dāng)緩沖區(qū)滿了或者遇到換行符(\n
)時(shí),fmt
包會(huì)將緩沖區(qū)的內(nèi)容寫入到標(biāo)準(zhǔn)輸出,確保完整地輸出所有的數(shù)據(jù)。
也就是說我有一個(gè)8k的打印內(nèi)容,而緩沖區(qū)大小為4KB,那么在第一次寫入緩沖區(qū)后,緩沖區(qū)將被填滿。此時(shí),fmt
包會(huì)進(jìn)行一次IO操作,將緩沖區(qū)的內(nèi)容寫入標(biāo)準(zhǔn)輸出。
由于緩沖區(qū)已滿,第二次寫入操作將觸發(fā)另一次IO操作,將剩余的內(nèi)容寫入標(biāo)準(zhǔn)輸出。
因此,在這種情況下,fmt
包會(huì)進(jìn)行兩次IO操作,將完整的8KB內(nèi)容寫入標(biāo)準(zhǔn)輸出。第一次是在緩沖區(qū)填滿后,第二次是在第二次寫入時(shí)。
到此這篇關(guān)于淺析Go語(yǔ)言中的緩沖區(qū)及其在fmt包中的應(yīng)用的文章就介紹到這了,更多相關(guān)Go緩沖區(qū)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang使用sync.singleflight解決熱點(diǎn)緩存穿透問題
在go的sync包中,有一個(gè)singleflight包,里面有一個(gè)?singleflight.go文件,代碼加注釋,一共200行出頭,通過?singleflight可以很容易實(shí)現(xiàn)緩存和去重的效果,避免重復(fù)計(jì)算,接下來我們就給大家詳細(xì)介紹一下sync.singleflight如何解決熱點(diǎn)緩存穿透問題2023-07-07以go為例探究beyla從環(huán)境變量BEYLA_OPEN_PORT發(fā)現(xiàn)進(jìn)程原理
這篇文章主要為大家介紹了以golang進(jìn)程為例,研究beyla從環(huán)境變量BEYLA_OPEN_PORT(即通過端口)發(fā)現(xiàn)進(jìn)程的原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12一文詳解golang延時(shí)任務(wù)的實(shí)現(xiàn)
這篇文章主要為大家介紹了golang延時(shí)任務(wù)的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03詳解Golang net/http包中的RoundTripper接口
RoundTripper 是 net/http 包中的一個(gè)接口,定義了處理 HTTP 請(qǐng)求返回和響應(yīng)的方法,是 http.Client 結(jié)構(gòu)體中執(zhí)行 http 請(qǐng)求的核心部分,本文將詳細(xì)的給大家介紹Golang RoundTripper接口,需要的朋友可以參考下2023-09-09Golang map實(shí)踐及實(shí)現(xiàn)原理解析
這篇文章主要介紹了Golang map實(shí)踐以及實(shí)現(xiàn)原理,Go 語(yǔ)言中,通過哈希查找表實(shí)現(xiàn) map,用鏈表法解決哈希沖突,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-06-06