golang中container/list包中的坑

golang中list包用法可以參看http://blog.csdn.net/chenbaoke/article/details/42780895

但是list包中大部分對於e *Element進行操作的元素都可能會導致程序崩潰,其根本原因是e是一個Element類型的指針,當然其也可能爲nil,但是golang中list包中函數沒有對其進行是否爲nil的檢查,變默認其非nil進行操作,所以這種情況下,便可能出現程序崩潰

1.舉個簡單例子,Remove()函數

package main

import (
	"container/list"
	"fmt"
)

func main() {
	l := list.New()
	l.PushBack(1)
	fmt.Println(l.Front().Value) //1
	value := l.Remove(l.Front())
	fmt.Println(value)            //1
	value1 := l.Remove(l.Front()) //panic: runtime error: invalid memory address or nil pointer dereference
	fmt.Println(value1)
}
從程序中可以直觀的看出程序崩潰,原因是list中只有1個元素,但是要刪除2個元素。但是再進一步查看一下原因,便會得出如下結果。

golang中Front()函數實現如下

func (l *List) Front() *Element {
    if l.len == 0 {
        return nil
    }
    return l.root.next
}

由此可見,當第一次刪除之後。list的長度變爲0,此時在調用l.Remove(l.Front()),其中l.Front()返回的是一個nil。


接下來再看golang中Remove()函數實現,該函數並沒有判定e是否爲nil,變直接默認其爲非nil,直接對其進行e.list或者e.Value取值操作。當e爲nil時,這兩個操作都將會造成程序崩潰,這也就是爲什麼上面程序會崩潰的原因。

func (l *List) Remove(e *Element) interface{} {
	if e.list == l {
		// if e.list == l, l must have been initialized when e was inserted
		// in l or l == nil (e is a zero Element) and l.remove will crash
		l.remove(e)
	}
	return e.Value
}


2.(l *list)PushBackList(other *list)該函數用於將other list中元素添加在l list的後面。基本實現思想是取出other中所有元素,將其順次掛載在l列表中,但是golang中實現有問題,代碼如下。

func (l *List) PushBackList(other *List) {
	l.lazyInit()
	for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
		l.insertValue(e.Value, l.root.prev)
	}
}
其具體思想是首先獲取other的長度n,然後循環n次取出其元素將其插入l中。問題就出現在循環n次,如果在這個過程中other的元素變化的話,例如其中有些元素被刪除了,這就導致e的指針可能爲nil,此時再利用e.Value取值,程序便會崩潰。如下所示。

package main

import (
	"container/list"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(8)
	l := list.New()
	ls := list.New()
	for i := 0; i < 10000; i++ {
		ls.PushBack(i)
	}
	go ls.Remove(l.Back())
	l.PushBackList(ls) //invalid memory address or nil pointer dereference
}
如程序中所示,再講ls中元素添加到l過程中,如果ls中元素減少,程序便會崩潰。原因如上面分析。


建議:

在golang中如果對與list的操作只有串行操作,則只需要注意檢查元素指針是否爲nil便可避免程序崩潰,如果程序中會併發處理list中元素,建議對list進行加寫鎖(全局鎖),然後再操作。注意,讀寫鎖無法保證並行處理list時程序的安全性。





發佈了41 篇原創文章 · 獲贊 24 · 訪問量 61萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章