Выпуск пакета "fmt" - PullRequest
       51

Выпуск пакета "fmt"

0 голосов
/ 20 января 2020

Я сталкиваюсь с проблемой, которая кажется простой, но которую я не могу воспроизвести, и поэтому не могу объяснить.

Эта проблема возникает в производстве, и таинственным является то, что она возникает очень редко (поэтому я не могу воспроизвести ее), это может быть фактором, который я не мог бы привести в качестве примера, но вот контекст:

type MyType struct {
    Field1 string
    Field2 int
    Field3 time.Time
    Field4 []float64
    // No pointers fields
}

func main() {
    var MyChan = make(chan interface{})

    go func() {
// This routine is reading and parsing messages from a WS dial and writing it in a channel
// There is 2 only possible answer in the channel : a string, or a "MyType" like (with more fields, but no pointers in)     
        var Object MyType
        Object.Field1 = "test"
        // ..

        MyChan <- Object
    }()

    go func() {
// This routine is sending a message to a WS dial and wait for the answer in a channel fed by another routine :
        var Object interface{}
        go func(Get *interface{}) {
            *Get = <- MyChan
        } (&Object)
        for Object == nil {
            time.Sleep(time.Nanosecond * 1)
        }
        log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func
    }()
}

Pani c трассировка стека:

runtime error: invalid memory address or nil pointer dereference
panic(0x87a840, 0xd0ff40)
    X:/Go/GoRoot/src/runtime/panic.go:522 +0x1b5
reflect.Value.String(0x85a060, 0x0, 0xb8, 0xc00001e500, 0xc0006f1a80)
    X:/Go/GoRoot/src/reflect/value.go:1781 +0x45
fmt.(*pp).printValue(0xc006410f00, 0x85a060, 0x0, 0xb8, 0x76, 0x1)
    X:/Go/GoRoot/src/fmt/print.go:747 +0x21c3
fmt.(*pp).printValue(0xc006410f00, 0x8ed5c0, 0x0, 0x99, 0x76, 0x0)
    X:/Go/GoRoot/src/fmt/print.go:796 +0x1b52
fmt.(*pp).printArg(0xc006410f00, 0x8ed5c0, 0x0, 0x76)
    X:/Go/GoRoot/src/fmt/print.go:702 +0x2ba
fmt.(*pp).doPrint(0xc006410f00, 0xc0006f22a0, 0x1, 0x1)
    X:/Go/GoRoot/src/fmt/print.go:1147 +0xfd
fmt.Sprint(0xc0006f22a0, 0x1, 0x1, 0x0, 0x0)
    X:/Go/GoRoot/src/fmt/print.go:250 +0x52

Go версия: 1.12.1 windows / amd64

Поблагодарив вас за потраченное время, я надеюсь, что кто-нибудь сможет объяснить мне, что случилось.

1 Ответ

1 голос
/ 20 января 2020

У вас есть гонка данных здесь:

    var Object interface{}
    go func(Get *interface{}) {
        *Get = <- MyChan
    } (&Object)
    for Object == nil {
        time.Sleep(time.Nanosecond * 1)
    }

Программа, записывающая в переменную Object, делает это без использования блокировки: она получает от канала, и когда он получает значение, записывает его в *Get, где Get == &Object, поэтому записывает значение интерфейса Object.

. Между тем, процедура, выполняющая for l oop, читает из Object, проверка на ноль. Он читает без использования блокировки, так что он может прочитать частично записанное значение.

На самом деле происходит то, что частично записанное значение не равно нулю, а целое значение не устанавливается. Таким образом, for l oop прекращает цикл, и код переходит к следующей строке:

    log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func

Поскольку Object только частично записано, доступ к его значению приводит к непредсказуемым результатам, но в этом случае он создает pani c. (В частности, было установлено поле type интерфейса, но поле value по-прежнему равно нулю. Фактический pani c из этой строки в src/reflect/value.go:

               return *(*string)(v.ptr)

с v.ptr, не установленным.)

Не ясно, зачем вы регистрируете значение вообще, ни почему вы используете совместно используемую память для связи, но если вы делаете , вам понадобится блокировка. Обычно разумнее этого не делать. См. Также этот ответ до Объясните: не общайтесь, разделяя память; делиться памятью, общаясь .

(Или, проще говоря, почему бы просто не использовать Object := <-MyChan вместо всей функции "круг и круг"?)

...