亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么這個 Go 程序會出現數據競賽?

為什么這個 Go 程序會出現數據競賽?

Go
DIEA 2021-07-08 17:07:10
我試圖將日志消息存儲在緩沖區中,以便僅在出現錯誤時訪問它們。有點像智能日志處理中的機會日志記錄。在這個例子中,我每 5 秒從緩沖區中獲取日志,但是當我使用go run -race code.go.我正在使用渠道進行交流,但顯然我做錯了。package mainimport (    "bytes"    "fmt"    "io/ioutil"    "log"    "time")type LogRequest struct {    Buffer chan []byte}type LogBuffer struct {    LogInputChan chan []byte    LogRequests  chan LogRequest}func (f LogBuffer) Write(b []byte) (n int, err error) {    f.LogInputChan <- b    return len(b), nil}func main() {    var logBuffer LogBuffer    logBuffer.LogInputChan = make(chan []byte, 100)    logBuffer.LogRequests = make(chan LogRequest, 100)    log.SetOutput(logBuffer)    // store the log messages in a buffer until we ask for it    go func() {        buf := new(bytes.Buffer)        for {            select {            // receive log messages            case logMessage := <-logBuffer.LogInputChan:                buf.Write(logMessage) // <- data race            case logRequest := <-logBuffer.LogRequests:                c, errReadAll := ioutil.ReadAll(buf)                if errReadAll != nil {                    panic(errReadAll)                }                logRequest.Buffer <- c            }        }    }()    // log a test message every 1 second    go func() {        for i := 0; i < 30; i++ {            log.Printf("test: %d", i) // <- data race            time.Sleep(1 * time.Second)        }    }()    // print the log every 5 seconds    go func() {        for {            time.Sleep(5 * time.Second)            var logRequest LogRequest            logRequest.Buffer = make(chan []byte, 1)            logBuffer.LogRequests <- logRequest            buffer := <-logRequest.Buffer            fmt.Printf("**** LOG *****\n%s**** END *****\n\n", buffer)        }    }()    time.Sleep(45 * time.Second)}
查看完整描述

1 回答

?
翻閱古今

TA貢獻1780條經驗 獲得超5個贊

該log包使用內部緩沖區來構建用于輸出的日志消息(log/Logger 中的buf字段)。它組成標題,附加調用者提供的數據,然后將此緩沖區傳遞給您的 方法以進行輸出。Write


為了減少分配,log包為每個日志消息回收這個緩沖區。文檔中沒有說明,但隱含的假設是您的Write方法僅[]byte在Write調用期間使用提供的數據。這個假設適用于大多數輸出,例如文件或 STDOUT。


為避免數據競爭,您需要在從Write函數返回之前對傳入數據進行顯式復制:


func (f LogBuffer) Write(b []byte) (n int, err error) {

    z := make([]byte, len(b))

    copy(z, b)

    f.LogInputChan <- z

    return len(b), nil

}


查看完整回答
反對 回復 2021-07-12
  • 1 回答
  • 0 關注
  • 228 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號