佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > 解決golang中container/list包中的坑

解決golang中container/list包中的坑

熱門(mén)標(biāo)簽:呼叫中心市場(chǎng)需求 地方門(mén)戶網(wǎng)站 服務(wù)外包 AI電銷(xiāo) 鐵路電話系統(tǒng) 百度競(jìng)價(jià)排名 網(wǎng)站排名優(yōu)化 Linux服務(wù)器

golang中l(wèi)ist包用法可以參看這篇文章

但是list包中大部分對(duì)于e *Element進(jìn)行操作的元素都可能會(huì)導(dǎo)致程序崩潰,其根本原因是e是一個(gè)Element類(lèi)型的指針,當(dāng)然其也可能為nil,但是golang中l(wèi)ist包中函數(shù)沒(méi)有對(duì)其進(jìn)行是否為nil的檢查,變默認(rèn)其非nil進(jìn)行操作,所以這種情況下,便可能出現(xiàn)程序崩潰。

1.舉個(gè)簡(jiǎn)單例子

Remove()函數(shù)

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個(gè)元素,但是要?jiǎng)h除2個(gè)元素。但是再進(jìn)一步查看一下原因,便會(huì)得出如下結(jié)果。

golang中Front()函數(shù)實(shí)現(xiàn)如下

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

由此可見(jiàn),當(dāng)?shù)谝淮蝿h除之后。list的長(zhǎng)度變?yōu)?,此時(shí)在調(diào)用l.Remove(l.Front()),其中l(wèi).Front()返回的是一個(gè)nil。

接下來(lái)再看golang中Remove()函數(shù)實(shí)現(xiàn),該函數(shù)并沒(méi)有判定e是否為nil,變直接默認(rèn)其為非nil,直接對(duì)其進(jìn)行e.list或者e.Value取值操作。

當(dāng)e為nil時(shí),這兩個(gè)操作都將會(huì)造成程序崩潰,這也就是為什么上面程序會(huì)崩潰的原因。

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)

該函數(shù)用于將other list中元素添加在l list的后面。

基本實(shí)現(xiàn)思想是取出other中所有元素,將其順次掛載在l列表中,但是golang中實(shí)現(xiàn)有問(wèn)題

代碼如下

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的長(zhǎng)度n,然后循環(huán)n次取出其元素將其插入l中。問(wèn)題就出現(xiàn)在循環(huán)n次,如果在這個(gè)過(guò)程中other的元素變化的話,例如其中有些元素被刪除了,這就導(dǎo)致e的指針可能為nil,此時(shí)再利用e.Value取值,程序便會(huì)崩潰。

如下所示

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過(guò)程中,如果ls中元素減少,程序便會(huì)崩潰。原因如上面分析。

建議:

在golang中如果對(duì)與list的操作只有串行操作,則只需要注意檢查元素指針是否為nil便可避免程序崩潰,如果程序中會(huì)并發(fā)處理list中元素,建議對(duì)list進(jìn)行加寫(xiě)鎖(全局鎖),然后再操作。注意,讀寫(xiě)鎖無(wú)法保證并行處理list時(shí)程序的安全性。

補(bǔ)充:golang list 鏈表

看代碼吧~

package main 
import (
	"container/list"
	"fmt"
)
 
func main() {
	dataList := list.New()
 
	dataList.PushBack(1)	// 插入末尾
	dataList.PushBack(2)
	dataList.PushFront(3)	 // 插入表頭
	dataList.PushBack(4)
	dataList.PushBack(5)
	m := dataList.PushBack(6)
	m1 := dataList.InsertBefore(7,m)	// 6 之前插入 7
	m2 := dataList.InsertAfter(8,m)	// 6 之后插入 8
 
	// 從鏈表頭開(kāi)始遍歷
	for e := dataList.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value) // 打印值
	}
 
	fmt.Println("----------------------------------------")
 
	dataList.Remove(dataList.Front())	// 移除頭部
	dataList.MoveBefore(m2, m)	// 將m2移動(dòng)m之前
	dataList.MoveAfter(m1, m)
	dataList.Remove(m)	// 移除
 
	//PushBackList	// 插入列表
	//PushFrontList	//
 
	// 從鏈表頭開(kāi)始遍歷
	for e := dataList.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value) // 打印值
	}
 
	fmt.Println("----------------------------------------")
 
 
	// 從鏈表尾開(kāi)始遍歷
	for e := dataList.Back(); e != nil; e = e.Prev() {
		fmt.Println(e.Value, " ")
	}
 
	fmt.Println("----------------------------------------")
	dataList.Init()	// 清空鏈表
	// 從鏈表頭開(kāi)始遍歷
	for e := dataList.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value) // 打印值
	}
}

運(yùn)行結(jié)果:

3
1
2
4
5
7
6
8
----------------------------------------
1
2
4
5
8
7
----------------------------------------
7  
8  
5  
4  
2  
1  
----------------------------------------

Process finished with exit code 0

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

您可能感興趣的文章:
  • linux內(nèi)核編程container of()函數(shù)介紹
  • Flutter通過(guò)Container實(shí)現(xiàn)時(shí)間軸效果
  • 使用 Azure Container Registry 儲(chǔ)存鏡像的問(wèn)題
  • 基于golang中container/list包的用法說(shuō)明
  • Docker 退出container后保持繼續(xù)運(yùn)行的操作
  • docker 移除掉運(yùn)行不正常的container操作
  • 再見(jiàn) Docker如何5分鐘轉(zhuǎn)型 containerd
  • C語(yǔ)言container of()函數(shù)案例詳解

標(biāo)簽:黃山 蘭州 仙桃 銅川 崇左 衡水 湘潭 湖南

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《解決golang中container/list包中的坑》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    航空| 凉城县| 阳东县| 长治市| 基隆市| 砀山县| 调兵山市| 安新县| 龙门县| 青岛市| 壶关县| 左贡县| 宣恩县| 土默特右旗| 凤阳县| 南开区| 郯城县| 福安市| 崇文区| 丰镇市| 益阳市| 利川市| 宜春市| 东乡| 浦城县| 禄劝| 黑龙江省| 环江| 许昌市| 习水县| 陕西省| 广昌县| 当涂县| 出国| 台州市| 遂宁市| 凤台县| 巩义市| 泗洪县| 永仁县| 三门县|