stdout 上的並發寫入:線程安全分析
在最近的一次討論中,出現了一段Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go 程式碼,引發了關於Go線程的爭論同時寫入標準輸出時的安全性。有問題的程式碼是:
<code class="go">package main import ( "fmt" "os" "strings" ) func main() { x := strings.Repeat(" ", 1024) go func() { for { fmt.Fprintf(os.Stdout, x+"aa\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"bb\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"cc\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"dd\n") } }() <-make(chan bool) }</code>
執行緒安全注意事項:
當多個goroutine 並發寫入stdout 時,會出現此程式碼是否為執行緒安全的問題。提到了有關此事的各種消息來源和意見,但未能找到明確的答案。讓我們進一步深入研究這個主題。
fmt 套件行為:
fmt 套件函數只需採用 io.Writer 實作並對其呼叫 Write() 即可。這些函數本身是線程安全的,這意味著對 fmt.F* 函數的多個並發呼叫是安全的。然而,並發寫入 stdout 的實作取決於所使用的特定「writer」。
「Writer」實作:
「writers」的兩個主要類別是相關的:
POSIX 語意:
對於檔案描述符,POSIX 需要 write (2) 在操作常規檔案或符號連結時呼叫原子性。這意味著在我們的例子中,假設 stdout 是檔案描述符,寫入呼叫應該是原子的。
Go 標準函式庫實作:
Go 標準函式庫的檔案描述子和套接字的包裝器旨在將寫入操作一對一對應到底層對象。這消除了 write 調用被拆分或粘合在一起的可能性。
結論:
基於 POSIX write(2) 呼叫的可用資訊和底層語義,所提供的程式碼不受資料競爭的影響。然而,寫入底層檔案描述符的輸出可能會以不可預測的順序混合。此行為受到作業系統核心版本、Go 版本、硬體和系統負載等因素的影響。
確保每個特定 fmt.Fprint* 調用的輸出在結果輸出中顯示為連續的片段,建議使用鎖或使用日誌包來序列化調用,日誌包提供了自己的鎖定機制。
以上是Go 中並發寫入「stdout」是線程安全的嗎? `fmt.Fprintf` 行為的詳細分析。的詳細內容。更多資訊請關注PHP中文網其他相關文章!