为什么这个 Go 程序会出现数据竞赛?

我试图将日志消息存储在缓冲区中,以便仅在出现错误时访问它们。有点像智能日志处理中的机会日志记录。在这个例子中,我每 5 秒从缓冲区中获取日志,但是当我使用go run -race code.go.


我正在使用渠道进行交流,但显然我做错了。


package main


import (

    "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)

}



DIEA
浏览 202回答 1
1回答

翻阅古今

该log包使用内部缓冲区来构建用于输出的日志消息(log/Logger 中的buf字段)。它组成标题,附加调用者提供的数据,然后将此缓冲区传递给您的 方法以进行输出。Write为了减少分配,log包为每个日志消息回收这个缓冲区。文档中没有说明,但隐含的假设是您的Write方法仅[]byte在Write调用期间使用提供的数据。这个假设适用于大多数输出,例如文件或 STDOUT。为避免数据竞争,您需要在从Write函数返回之前对传入数据进行显式复制:func (f LogBuffer) Write(b []byte) (n int, err error) {&nbsp; &nbsp; z := make([]byte, len(b))&nbsp; &nbsp; copy(z, b)&nbsp; &nbsp; f.LogInputChan <- z&nbsp; &nbsp; return len(b), nil}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go