1 回答

TA貢獻(xiàn)1794條經(jīng)驗(yàn) 獲得超8個(gè)贊
我不確定它是否有資格作為一個(gè)明確的答案,但我會(huì)嘗試提供一些見解。
包的F*-functionsfmt僅聲明它們采用實(shí)現(xiàn)io.Writer接口的類型的值并調(diào)用Write它。函數(shù)本身對(duì)于并發(fā)使用是安全的——從某種意義上說(shuō),可以同時(shí)調(diào)用任意數(shù)量的函數(shù)fmt.Fwhaveter:包本身已經(jīng)為此做好了準(zhǔn)備,但是當(dāng)同時(shí)寫入實(shí)現(xiàn)的類型的相同值時(shí)io.Writer,問(wèn)題變得更加復(fù)雜因?yàn)樵?Go 中對(duì)接口的支持并沒(méi)有說(shuō)明真正的并發(fā)類型。
換句話說(shuō),可能允許或不允許并發(fā)的真正點(diǎn)被推遲到寫入函數(shù)的“作者” fmt。(還應(yīng)該記住,這些fmt.*Print*函數(shù)可以Write在一次調(diào)用期間連續(xù)多次調(diào)用其目的地,而不是那些由 stock 包提供的函數(shù)log。)
所以,我們基本上有兩種情況:
的自定義實(shí)現(xiàn)io.Writer。
它的股票實(shí)現(xiàn),例如*os.File由 package.json 函數(shù)產(chǎn)生的套接字或圍繞套接字的net包裝器。
第一種情況很簡(jiǎn)單:無(wú)論實(shí)現(xiàn)者做了什么。
第二種情況更難:據(jù)我了解,Go 標(biāo)準(zhǔn)庫(kù)對(duì)此的立場(chǎng)(盡管文檔中沒(méi)有明確說(shuō)明)因?yàn)樗鼑@操作系統(tǒng)提供的“事物”(例如文件描述符和套接字)提供的包裝是合理的“瘦”,因此無(wú)論它們實(shí)現(xiàn)什么語(yǔ)義,都由運(yùn)行在特定系統(tǒng)上的 stdlib 代碼傳遞實(shí)現(xiàn)。
例如,POSIX要求write(2) 在對(duì)常規(guī)文件或符號(hào)鏈接進(jìn)行操作時(shí),調(diào)用彼此之間是原子的。這意味著,由于Write對(duì)包裝文件描述符或套接字的事物的任何調(diào)用實(shí)際上都會(huì)導(dǎo)致目標(biāo)系統(tǒng)的單個(gè)“寫入”系統(tǒng)調(diào)用,因此您可以查閱目標(biāo)操作系統(tǒng)的文檔并了解會(huì)發(fā)生什么。
請(qǐng)注意,POSIX 只告訴文件系統(tǒng)對(duì)象,如果os.Stdout打開到終端(或偽終端)或管道或任何其他支持write(2)系統(tǒng)調(diào)用的東西,結(jié)果將取決于相關(guān)子系統(tǒng)和/或驅(qū)動(dòng)程序?qū)崿F(xiàn)——例如,來(lái)自多個(gè)并發(fā)調(diào)用的數(shù)據(jù)可能散布在其中,或者其中一個(gè)調(diào)用或兩者都可能被操作系統(tǒng)失敗——不太可能,但仍然如此。
回到 Go,根據(jù)我的收集,以下事實(shí)適用于包裝文件描述符和套接字的 Go stdlib 類型:
它們本身可以安全地并發(fā)使用(我的意思是,在 Go 級(jí)別上)。
它們“映射”Write并Read一對(duì)一地調(diào)用底層對(duì)象——也就是說(shuō),一個(gè)Write調(diào)用永遠(yuǎn)不會(huì)分成兩個(gè)或多個(gè)底層系統(tǒng)調(diào)用,并且一個(gè)Read調(diào)用永遠(yuǎn)不會(huì)從多個(gè)底層系統(tǒng)調(diào)用的結(jié)果中返回“粘合”的數(shù)據(jù)。(順便說(shuō)一句,人們偶爾會(huì)被這種樸素的行為絆倒——例如,把這個(gè)或這個(gè)作為例子。)
因此,基本上,當(dāng)我們考慮這一點(diǎn)時(shí),每次fmt.*Print*調(diào)用都可以自由調(diào)用Write任意次數(shù),您使用os.Stdout, 的示例將:
永遠(yuǎn)不要導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)——除非你已經(jīng)為變量分配了os.Stdout一些自定義實(shí)現(xiàn)——但是
實(shí)際寫入底層 FD 的數(shù)據(jù)將以不可預(yù)知的順序混合,這可能取決于許多因素,包括操作系統(tǒng)內(nèi)核版本和設(shè)置、用于構(gòu)建程序的 Go 版本、硬件和系統(tǒng)上的負(fù)載。
TL;博士
多個(gè)并發(fā)調(diào)用fmt.Fprint*寫入相同的“writer”值將它們的并發(fā)性推遲到“writer”的實(shí)現(xiàn)(類型)。
在您在問(wèn)題中提出的設(shè)置中,不可能與 Go stdlib 提供的“類文件”對(duì)象進(jìn)行數(shù)據(jù)競(jìng)爭(zhēng)。
真正的問(wèn)題不在于 Go 程序級(jí)別的數(shù)據(jù)競(jìng)爭(zhēng),而是在 OS 級(jí)別發(fā)生的對(duì)單個(gè)資源的并發(fā)訪問(wèn)。在那里,我們(通常)不談?wù)摂?shù)據(jù)競(jìng)爭(zhēng),因?yàn)?Go 支持的商品操作系統(tǒng)將人們可能“寫入”的東西暴露為抽象,其中真正的數(shù)據(jù)競(jìng)爭(zhēng)可能表明內(nèi)核或驅(qū)動(dòng)程序中存在錯(cuò)誤(以及Go 的競(jìng)爭(zhēng)檢測(cè)器無(wú)論如何都無(wú)法檢測(cè)到它,因?yàn)樵搩?nèi)存不會(huì)由為進(jìn)程提供動(dòng)力的 Go 運(yùn)行時(shí)擁有)。
基本上,在您的情況下,如果您需要確保任何特定調(diào)用生成的數(shù)據(jù)fmt.Fprint*作為操作系統(tǒng)提供的實(shí)際數(shù)據(jù)接收器的單個(gè)連續(xù)片段出現(xiàn),您需要序列化這些調(diào)用,因?yàn)閒mt包不提供關(guān)于Write為其導(dǎo)出的函數(shù)調(diào)用提供的“編寫器”的次數(shù)。
序列化可以是外部的(顯式的,即“獲取鎖,調(diào)用fmt.Fprint*,釋放鎖”)或內(nèi)部的——通過(guò)os.Stdout將管理鎖的自定義類型包裝并使用它)。當(dāng)我們?cè)谧龅臅r(shí)候,log包就是這樣做的,并且可以直接用作它提供的“記錄器”,包括默認(rèn)的,允許禁止輸出“日志頭”(例如時(shí)間戳和文件名)。
- 1 回答
- 0 關(guān)注
- 217 瀏覽
添加回答
舉報(bào)