douxianxing5712 2013-12-19 17:06
浏览 37
已采纳

为什么此Go程序中存在数据竞赛?

I'm trying to store log messages in a buffer to access them only when I get an error. A bit like in Smarter log handling, the case for opportunistic logging. In this example I fetch the logs from the buffer each 5 seconds but I get a data race when I run it with go run -race code.go.

I'm using channels to communicate but I'm doing something wrong, obviously.

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 *****
%s**** END *****

", buffer)
        }
    }()

    time.Sleep(45 * time.Second)
}
  • 写回答

1条回答 默认 最新

  • dongruo4601 2013-12-19 18:07
    关注

    The log package uses an internal buffer to build-up log messages for output (the buf field in log/Logger). It composes the header, appends the data provided by the caller, then passes this buffer to your Write method for output.

    In order to reduce allocations, the log package recycles this buffer for each log message. It's not stated in the documentation, but the implicit assumption is that your Write method only uses the provided []byte data for the duration of the Write call. This assumption is OK for most outputs, e.g. a file or STDOUT.

    To avoid a data race, you need to make an explicit copy of the incoming data before returning from the Write function:

    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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 怎么获取下面的: glove_word2id.json和 glove_numpy.npy 这两个文件
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误
  • ¥15 一道python难题3
  • ¥15 牛顿斯科特系数表表示
  • ¥15 arduino 步进电机
  • ¥20 程序进入HardFault_Handler
  • ¥15 oracle集群安装出bug