go select 和for Go語言常見坑

2021-10-13 17:24:05 字數 3696 閱讀 3245

這裡列舉的go語言常見坑都是符合go語言語法的,可以正常的編譯,但是可能是執行結果錯誤,或者是有資源洩漏的風險。

當引數的可變引數是空介面型別時,傳入空介面的切片時需要注意引數展開的問題。

func main() 

fmt.println(a)

fmt.println(a...)

}

不管是否展開,編譯器都無法發現錯誤,但是輸出是不同的:

[1 2 3]

1 2 3

在函式呼叫引數中,陣列是值傳遞,無法通過修改陣列型別的引數返回結果。

func main() 

func(arr [3]int) (x)

fmt.println(x)

}

必要時需要使用切片。

map是一種hash表實現,每次遍歷的順序都可能不一樣。

func main() 

for k, v := range m

}

在區域性作用域中,命名的返回值內同名的區域性變數遮蔽:

func foo() (err error) 

return

}

recover捕獲的是祖父級呼叫時的異常,直接呼叫時無效:

func main() 

直接defer呼叫也是無效:

func main() 

defer呼叫時多層巢狀依然無效:

func main() ()

}()panic(1)

}

必須在defer函式中直接呼叫才有效:

func main() ()

panic(1)

}

後台goroutine無法保證完成任務。

func main() 

休眠並不能保證輸出完整的字串:

func main() 

類似的還有通過插入排程語句:

func main() 

goroutine是協作式搶占排程,goroutine本身不會主動放棄cpu:

func main() 

}()for {} // 占用cpu

}

解決的方法是在for迴圈加入runtime.gosched()排程函式:

func main() 

}()for

}

或者是通過阻塞的方式避免cpu占用:

func main() 

os.exit(0)

}()select{}

}

因為在不同的goroutine,main函式中無法保證能列印出hello, world:

var msg string

var done bool

func setup()

func main()

println(msg)

}

解決的辦法是用顯式同步:

var msg string

var done = make(chan bool)

func setup()

func main()

msg的寫入是在channel傳送之前,所以能保證列印hello, world

func main() ()}}

改進的方法是在每輪迭代中生成乙個區域性變數:

func main() ()}}

或者是通過函式引數傳入:

func main() (i)}}

defer在函式退出時才能執行,在for執行defer會導致資源延遲釋放:

func main() 

defer f.close()}}

解決的方法可以在for中構造乙個區域性函式,在區域性函式內部執行defer:

func main() 

defer f.close()

}()}

}

切片會導致整個底層陣列被鎖定,底層陣列無法釋放記憶體。如果底層陣列較大會對記憶體產生很大的壓力。

func main() 

headermap[name] = data[:1]

}// do some thing

}

解決的方法是將結果轉殖乙份,這樣可以釋放底層的陣列:

比如返回了乙個錯誤指標,但是並不是空的error介面:

func returnserror() error 

return p // will always return a non-nil error.

}

func main() 

當記憶體傳送變化的時候,相關的指標會同步更新,但是非指標型別的uintptr不會做同步更新。

同理cgo中也不能儲存go物件位址。

go語言是帶記憶體自動**的特性,因此記憶體一般不會洩漏。但是goroutine確存在洩漏的情況,同時洩漏的goroutine引用的記憶體同樣無法被**。

func main() 

} ()

return ch

}()for v := range ch }}

上面的程式中後台goroutine向管道輸入自然數序列,main函式中輸出序列。但是當break跳出for迴圈的時候,後台goroutine就處於無法被**的狀態了。

我們可以通過context包來避免這個問題:

func main() 

}} ()

return ch

}(ctx)

for v := range ch }}

當main函式在break跳出迴圈時,通過呼叫cancel()來通知後台goroutine退出,這樣就避免了goroutine的洩漏。

好文點贊收藏

mysql和sqlserver的sql語句區別

mysql 檢視系統內所有資料庫 show databases 查詢資料庫內所有表 show tables 顯示表結構 desc 表名 sqlserver 檢視系統內所有資料庫 select name,database id,create date from sys.databases 查詢資料庫內...

MySQL和Sql Server的sql語句區別

1 自增長列的插入 sqlserver中可以不為自動增長列插入值,mysql中需要為自動增長列插入值。2 獲取當前時間函式 sqlserver寫法 getdate mysql寫法 now 3 從資料庫定位到表。sqlserver寫法 庫名.dbo.表名 或者 庫名.表名 注 中間使用兩個點 sele...

Hive支援Update和Delete語句

文件說了hive從0.14開始支援的update和delete語句,我使用的hive是1.2.1版本。首先在hive site.xml裡配置如下屬性 hive.optimize.sort.dynamic.partition false hive.support.concurrency true hi...