`go tool vet -copylocks` 命令簡介Go 語言安裝包附帶 [vet](https://golang.org/cmd/vet/) 命令列工具。該工具能對程式源碼運行一套啟發學習法演算法以發現可疑的程式結構,如無法執行的代碼或對 `fmt.Printf` 函數的錯誤調用(指 arguments 沒有對齊 format 參數):```gopackage mainimport "fmt"func f() { fmt.Printf("%d\n") return fmt.Println("Done")}``````> go tool vet vet.govet.go:8: unreachable codevet.go:6: missing argument for Printf("%d"): format reads arg 1, have only 0 args```本文專講該工具的 copylocks 選項。讓我們看看它能做什麼以及如何在實際的程式中發揮作用。假設程式使用互斥鎖進行同步:```gopackage mainimport "sync"type T struct { lock sync.Mutex}func (t *T) Lock() { t.lock.Lock()}func (t T) Unlock() { t.lock.Unlock()}func main() { t := T{lock: sync.Mutex{}} t.Lock() t.Unlock() t.Lock()}```> 如果變數 v 是可定址的,並且 &v 的方法集合包含 m,那麼 v.m() 是 (&v).m() 的簡寫。想一想上述程式啟動並執行結果可能是什麼...程式會進入死結狀態:```fatal error: all goroutines are asleep — deadlock!goroutine 1 [semacquire]:sync.runtime_Semacquire(0x4201162ac) /usr/local/go/src/runtime/sema.go:47 +0x30sync.(*Mutex).Lock(0x4201162a8) /usr/local/go/src/sync/mutex.go:85 +0xd0main.(*T).Lock(0x4201162a8)...```運行上述程式得到了糟糕的結果,根本原因是把 receiver 按值傳遞給 Unlock 方法,所以 `t.lock.Unlock()` 實際上是由 lock 的副本調用的。我們很容易忽視這點,特別在更大型的程式中。Go 編譯器不會檢測這方面,因為這可能是程式員有意為之。該 vet 工具登場啦...```> go tool vet vet.govet.go:13: Unlock passes lock by value: main.T```選項 copylocks (預設啟用) 會檢測擁有 Lock 方法 (實際需要 pointer receiver) 的 type 是否按值傳遞。如果是這種情況,則會發出警告。sync 包有使用該機制的例子,它有一個命名為 noCopy 的特殊 type。為了避免某 type 按值拷貝 (實際上通過 vet 工具進行檢測),需要往 struct 定義中添加一個 field(如 WaitGroup):```gopackage mainimport "sync"type T struct { wg sync.WaitGroup}func fun(T) {}func main() { t := T{sync.WaitGroup{}} fun(t)}``````> go tool vet lab.golab.go:9: fun passes lock by value: main.T contains sync.WaitGroup contains sync.noCopylab.go:13: function call copies lock value: main.T contains sync.WaitGroup contains sync.noCopy```深入理解該機制![under-the-hood](https://raw.githubusercontent.com/studygolang/gctt-images/master/Detect-Locks-Passed-by-Value-in-Go/under-the-hood.jpeg)vet 工具的源檔案放在 `/src/cmd/vet` 路徑下。vet 的每個選項都利用 register 函數進行註冊,該函數其中兩個參數分別是一個可變參數 (類型是該選項所關注的 AST 結點類型) 和一個回呼函數。該回呼函數將因特定類型的結時間點事件觸發。對於 copylocks 選項,需要檢測的結點包含 return 語句。最終都會轉到 lockPath,它驗證傳遞的值是否屬於某個 type(擁有一個需要 pointer receiver 的 Lock 方法)。在整個處理過程中,go/ast 包被廣泛使用。可以在 Go 源碼可測試的樣本中找到對該包的簡單介紹。多點擊下方的 "" 按鈕, 以協助其他人找到這篇文章哦。如果您想獲得有關新文章的更新或未來工作進展的訊息, 請在這兒或者 Twitter 上關注我。
via: https://medium.com/golangspec/detect-locks-passed-by-value-in-go-efb4ac9a3f2b
作者:Michał Łowicki 譯者:mbyd916 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
197 次點擊