深度阅读之《Mastering Go》
寫在前面:這本書前前后后花了挺長時間,去年 11 月份就開始讀了,中間又斷了,直到最近才撿起來看完。
本書講得內容非常全面,語言也很順暢,生詞非常少,并且內容沒有太大難度,看起來比較過癮,算是全面復習一下 Go 語言。如果你想開始練習閱讀英文書,這本將是一個非常好的開始。
下面是閱讀過程中記錄的一些有用的點,隨意看看就好。
Go 有很多優點,其中一點是沒有預編譯階段,這使得它的編譯速度更快。像 C 語中,以 # 開頭的會被預編譯器處理。有預編譯器的語言有:C, C++, Ada, and PL/SQL。預編譯器的一大缺點是它會修改源代碼,而人們不知道送到編譯器里的最終的代碼是什么。
可以直接在命令行執行 go doc strings.Fields 獲取庫函數的解釋;執行 go get golang.org/x/tools/cmd/godoc 會安裝 godoc 工具,注意這兩者是不同的。前者是 go 命令,后者則是 godoc 命令。執行 godoc -http :8080 可以啟動一個 server,訪問 http://localhost:8080/pkg/ 即可看到 Go 的文檔。
執行 go build 會顯示生成一個可執行文件,僅僅一個 hello_world 就會達到 2M 大小,這是因為 Go 是靜態鏈接,生成的文件可以直接執行,不需要再動態鏈接其他文件。而執行 go run 命令,雖然也會生成可執行文件,但是它是隱式的,之后當程序執行完后會被自動刪掉。注意,看不見并不等于不存在!
所有的 UNIX 系統都支持:/dev/stdin、/dev/stdout、/dev/stderr 這三個特殊的文件名,它們也可以用 0、1、2 號文件描述符來描述。
fmt.Println(), fmt.Print(), and fmt.Printf() 用于打印,fmt.Sprintln(), fmt.Sprint(), fmt.Sprintf() 用于生成字符串,fmt.Fprintln(), fmt.Fprint(), fmt.Fprintf() 用于寫文件。
短賦值符 := 不能用于函數之外,因此全局變量只能用 var 聲明。
下面的代碼用于從標準輸入讀取數據,每讀出一行就打印出來:
按 ctrl+D 退出循環,因為 ctrl+D 會告知程序沒有更多的數據可以讀取。
os.Args 可以記錄通過命令行輸入的參數,并且它的類型是 []string,第一個元素是程序名,之后的為輸入參數。例如:
os.Args 為 [/tmp/go-build059507490/b001/exe/cla 10 1]
關于 docker 的命令:
關于 Go 的垃圾回收算法:并發標記清除、非分代、非整理,使用寫屏障。
Go 為了降低 GC 的停頓時間,讓 GC 和用戶程序并發執行。為了讓三色標記的結果不受并發執行的程序的影響,在整個標記過程中,要確保一個不變性:黑色集合里的對象保證不會指向白色集合里的對象,注意這并不影響一個白色對象指向黑色對象。我們把用戶程序稱為 mutator,mutator 運行了一個 writer barrier,每次當堆上有對象的指針(如果是對象的非指針字段變化,不影響)發生了變化,說明此對象可達,就要運行 writer barrier,將它變成灰色。mutator 通過 writer barrier 保證“黑色集合里的對象保證不會指向白色集合里的對象”這一不變性。這會帶來性能的損耗,但這是并發執行用戶程序和 GC 的代價。
垃圾回收器會在 channel 不可達時回收它,即使 channel 還未關閉。
time go run xx.go 可以計算運行程序花費的時間。
Please remember that at the end of the day, all programs that work on UNIX machines end up using C system calls to communicate with the UNIX kernel and perform most of their tasks. 所有在 UNIX 系統上運行的程序最終都會通過 C 系統調用來和內核打交道。用其他語言編寫程序進行系統調用,方法不外乎兩個:一是自己封裝,二是依賴 glibc、或者其他的運行庫。Go 語言選擇了前者,把系統調用都封裝到了 syscall 包。封裝時也同樣得通過匯編實現。
strace ls 查看都有哪些系統調用,-c 可以計數。
將 .go 文件轉化成匯編代碼時,可指定操作系統和架構:
GOOS 和 GOARCH 可選項為:The list of valid GOOS values includes android, darwin, dragonfly, freebsd, linux, nacl, netbsd, openbsd, plan9, solaris, windows, and zos. On the other hand, the list of valid GOARCH values includes 386, amd64, amd64p32, arm, armbe, arm64, arm64be, ppc64, ppc64le, mips, mipsle, mips64, mips64le, mips64p32, mips64p32le, ppc, s390, s390x, sparc, and sparc64.
go build -x defer.go 展示 build 過程。
數組可以用 “:” 變成切片:array4[0:] 或 array4[:],copy 函數只接收切片作為參數。
什么時候使用指針:1. 可以 share data,尤其是在函數之間;2. 區別某個變量是未設置還是真的零值。
關于 strings 有很多有意思的方法,例如 Repeat, Fields 等等,在這里[1]可以看到很多。
Go container 包有 heap/list/ring 這幾個組件。
math/rand 可用于生成偽隨機數;更安全的生成隨機數:crypto/rand。
關于可變參數的函數(A variadic function),...Type 稱為 pack operator,而 Slice... 則被稱為 unpack operator。一個可變參數的函數只能使用一次 pack operator。
安裝一個本地包:
編譯一個本地包:
$?go?tool?compile?aPackage.go $?ls?-l?aPackage.* -rw-r--r--@?1?mtsouk?staff?201?Jan?10?22:08?aPackage.go?-rw-r--r--?1?mtsouk?staff?16316?Mar?4?20:01?aPackage.o關于 Go 版本,例如 v1.2.3,v1/v2/v3 通常是不兼容的,1 表示大版本,2 表示 feature,3 表示 fix。
如何和 gomod 工作:
創建 v2 版本:
使用 go mod vendor 命令來將依賴放到 vendor 文件夾里:
查找哪些 go 源文件使用了 syscall:
要記住的是在絕大部分程序里不需要使用反射,所以我們得弄清楚為什么反射是必須的以及什么時候需要使用反射。反射在實現 fmt, text/template, html/template 時是必須的。例如在 fmt 包里,反射可以讓你不需要明確處理所有的類型,你當然可以明確處理你知道的所有類型,但你仍然不可能處理 All possible types。
什么時候用反射:Therefore, you might need to use reflection when you want to be as generic as possible or when you want to make sure that you will be able to deal with data types that do not exist at the time of writing your code but might exist in the future. Additionally, reflection is handy when working with values of types that do not implement a common interface.
反射不好的三點:a. 大量的反射會造成程序代碼難以理解和維護。一個可行的解決方法是清晰的文檔注釋,但眾所周知,程序員是最不愿意寫文檔的人;b. 相比正常的數據結構,反射是動態地“決定”數據結構,因此會更慢。這些動態代碼也會使得一些代碼工具更難執行重構和分析;c. 反射的錯誤在 build 期間不會被捕獲,很多都是在運行期間直接 crash 整個程序。而且這經常是在程序正常運行數月甚至是數年之后才會爆發。一個可行的辦法是大量的測試,但這也不太可能覆蓋完全,并且會讓代碼庫更加龐大。
Go 不是一門面向對象的語言,但它可以模擬面向對象語言的某些功能。
flag.var 可以解析用逗號分隔的多個值。
wc 命令的結果有三列,分別表示行數、word 數,以及字節數。平時用的最多的是 wc -l,表示行數;wc -w 表示 wrod 數;wc -c 表示字節數。
如何輸出一個文件的權限,上代碼:
crtl+C 向進程發送 SIGINT 信號。Unix 里的信號其實都是軟中斷,用來異步處理“事件”,信號可以通過 name 和 number 來識別。進程不可能處理所有類型的信號,有些信號不能被 caught,不能被 blocked,例如 SIGKILL、SIGSTOP 不能被 caught,不能被 blocked,也不能被 ignored。因為它們給內核和 root 用戶提供了特權,可以停止運行某些進程。一般我們建議用信號的 name 來操作,例如 kill -s INT pid。有個例外的是 SIGKILL,它對應的 number 是 9,例如我們經常執行 kill -9 pid 來殺死某個進程,它等價于 kill -s KILL pid。
最常用來發送信號的方式用 kill 命令,默認發送的是 SIGTERM 信號。kill -l 命令可以列出所有支持的信號。
go run -race xx.go 可以顯示有競爭沖突的代碼。
diff pipeline.go plNoRace.go --color 顯示兩個文件的 diff。
Go 語言的并發模型是 fork-join 型的。使用 go 關鍵字啟動子協程工作,使用 sync.Wait 和 channel 來收集結果。
可通過設置環境變量來改變 runtime.GOMAXPROCS(0) 的輸出值:export GOMAXPROCS=800;。
sync.RWMutex 結構體里包含 sync.Mutex,即“讀寫鎖”是在“鎖”的基本上實現的。只有當所的讀鎖都 Unlock 了,寫鎖才能被 Lock。
time go run xx.go 可以顯示執行時間,包括 real, sys, user 的執行時間。
如果子 context 取消了,父 context 沒有收到消息,那么在父 context 取消前就發生了內存泄露。
For garbage collection to work correctly, the parent goroutine needs to keep a reference to each child goroutine. If a child goroutine ends without the parent knowing about it, then a memory leak occurs until the parent is canceled as well.
TAOCP——《計算機程序設計藝術》的作者高德納(Donald Ervin Knuth)老爺子的一句經典的話:
"The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming."
告訴我們不要老想著性能優化,在真的出現問題、出現瓶頸的時候再來考慮。
還有 Erlang 的作者之一 Joe Armstrong:
"Make it work, then make it beautiful, then if you really, really have to, make it fast. 90 percent of the time, if you make it beautiful, it will already be fast. So really, just make it beautiful!"
這告訴我們性能優化并不是主要工作,我們不要花費大量精力在這上面。
做優化的前提是程序沒有 bug,所以如果你在程序的第一版就來優化是有問題的,因為 v1 版本可能經常有 bug。
交叉編譯命令:env GOOS=linux GOARCH=386 go build xCompile.go。指定操作系統、指令集。
通過 bytes 包[2]的例子,可以看懂 godoc 和源碼[3]里的 comments 的對應關系。pkg/bytes 文檔里有很多代碼樣例,還可以 run 一下,但其實這些樣例是寫死在源碼里的,就在 src/bytes/example_test.go 文件里。一開始沒發現這個文件,我直接拿樣例代碼全局搜,一下就找到了。例如,源碼寫的如下兩個 example:
在 pkg[4] 上,就對應這樣:
go pkg example參考資料
[1]
這里: https://github.com/PacktPublishing/Mastering-Go-Second-Edition/blob/master/ch04/useStrings.go
[2]bytes 包: https://golang.org/pkg/bytes/
[3]源碼: https://golang.org/src/bytes/bytes.go
[4]pkg: https://golang.org/pkg
資料分享,關注公眾號回復指令:
回復【加群】,和大佬們一起成長。
回復【000】,下載一線大廠簡歷模板。
回復【001】,送你 Go 開源電子書。
總結
以上是生活随笔為你收集整理的深度阅读之《Mastering Go》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 几十万实例线上系统的抖动问题定位
- 下一篇: 时钟源为什么会影响性能