Go 了解戈朗恐慌

Go 了解戈朗恐慌,go,Go,我有一些Golang代码,非常间歇性(每隔几个小时一次),导致恐慌,我需要一些指导,了解如何找到原因。代码如下(带有行号): …并且,为了完成这张图片,有一个单独的定时的go func(),它处理newDatagramList,如下所示: go func() { var next *list.Element for _ = range processTicker.C { for newElement := newDatagramList.Front(); newE

我有一些Golang代码,非常间歇性(每隔几个小时一次),导致恐慌,我需要一些指导,了解如何找到原因。代码如下(带有行号):

…并且,为了完成这张图片,有一个单独的定时的
go func()
,它处理
newDatagramList
,如下所示:

go func() {
    var next *list.Element
    for _ = range processTicker.C {
        for newElement := newDatagramList.Front(); newElement != nil; newElement = next {
            next = newElement.Next();
            myProcessingFunction(newElement.Value.(*MyThing))
            newDatagramList.Remove(newElement)
        }
}
紧急输出为:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x5702d1]

goroutine 12 [running]:
panic(0x76e520, 0xc82000e100)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)
/usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1
main.operateAudioProcessing.func2(0xc8200164e0, 0xc82000f310, 0xc820024078, 0x240)
/home/rob/gocode/src/audio-process.go:420 +0x58b
created by main.operateAudioProcessing
/home/rob/gocode/src/audio-process.go:442 +0x5ba
恐慌告诉我是哪件事的错?最初没有人抱怨分配和发送到频道,所以我不认为这有什么错
newDatagramList
已明确初始化(该频道已运行并接收消息一段时间)

我该如何确定是什么让我爆炸?

恐慌堆栈:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x5702d1]

goroutine 12 [running]:
panic(0x76e520, 0xc82000e100)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)
/usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1
main.operateAudioProcessing.func2(0xc8200164e0, 0xc82000f310, 0xc820024078, 0x240)
/home/rob/gocode/src/audio-process.go:420 +0x58b
created by main.operateAudioProcessing
/home/rob/gocode/src/audio-process.go:442 +0x5ba
表示“无效内存地址或零指针取消引用”正在顶部列出的帧中发生

 container/list.(*List).PushBack(0xc820054e40, 0x6c4f80, 0xc827294e20, 0xc8273e0b01)

看起来是


这很酷,因为在这个框架中,它看起来在
l.root.prev上出错

容器/list
上构建的数据类型是完全不同步的,并且存在许多go例程的交错,这可能导致中间状态,其中列表元素指针同时被添加和删除

例如,
remove
有许多不同的操作:

func (l *List) remove(e *Element) *Element {
    e.prev.next = e.next
    e.next.prev = e.prev
    e.next = nil // avoid memory leaks
    e.prev = nil // avoid memory leaks
    e.list = nil
    l.len--
    return e
}

如果这是同步执行的,就不会有问题,但因为多个go例程正在对其进行操作

假设我们有一个元素
E
,有一个指向上一个和下一个的指针,还有两个goroutine作用于它

PREV - E - NEXT  

GOROUTINE1                                           GOROUTINE2

READ - E.PREV returns element (E2) with `NEXT` -> E

                                                    REMOVE(E) is called
                                                    e.next = nil // avoid memory leaks

E2.NEXT.NEXT access occurs now Nil!!! 
resulting in panic 

我认为恐慌是说问题在列表包中的Pushback方法中。有点怀疑newDatagramList是一个全局包。可能是竞争条件,因为列表不是线程安全的-是否从其他goroutine访问newDatagramList?您可以使用
go run-race
旋转应用程序,查看是否存在数据竞争。看起来“容器/列表”上没有任何同步。我想知道
go func()
循环是否试图处理和删除添加了一半的项?我很确定根本原因是
container/list
访问不是线程安全的,
remove()<代码> >推送< /代码> /代码>前面< /代码> />代码>下< /COD>由于不同步访问而创建竞争条件。我想这告诉我们Golang是如何实现信道和<代码> GO函数()的:<代码> PurthBuffE()/代码>必须发生在<代码> Read()/<代码>的中间(我想),所以这些不是哑循环,而是正确的线程。数据每20毫秒到达一次,而
go func()
进程循环每20毫秒运行一次,但问题发生需要一到两个小时(即几百万个事件)。无论如何,我希望互斥可以解决这个问题。可能Golang的伙计们应该声明
容器/list
不是线程安全的,以此作为对其他人的警告。谢谢你的帮助@罗布!!!对我在谷歌上搜索他们godoc中的线程安全性。我很惊讶他们没有明确提到我在Github上为他们添加了一个文档问题。。。。但他们不认为这是必要的,因为Golang的大多数类似C的函数同样不是线程安全的。只需要记住,Golang在很多方面都是优秀ole C前面的解析器。这是一个优势,千万不要忘记。确认在使用
sync.Lock()/Unlock()
保护运行3个小时后,问题没有再次出现。
 /usr/lib/go-1.6/src/container/list/list.go:139 +0x1c1
// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *List) PushBack(v interface{}) *Element {
    l.lazyInit()
    return l.insertValue(v, l.root.prev)
}
func (l *List) remove(e *Element) *Element {
    e.prev.next = e.next
    e.next.prev = e.prev
    e.next = nil // avoid memory leaks
    e.prev = nil // avoid memory leaks
    e.list = nil
    l.len--
    return e
}
PREV - E - NEXT  

GOROUTINE1                                           GOROUTINE2

READ - E.PREV returns element (E2) with `NEXT` -> E

                                                    REMOVE(E) is called
                                                    e.next = nil // avoid memory leaks

E2.NEXT.NEXT access occurs now Nil!!! 
resulting in panic