日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Go_入坑笔记

發(fā)布時(shí)間:2024/8/1 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go_入坑笔记 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

title: go-入坑筆記
categories: Go
tags: [go, 編程]
date: 2018-08-02 20:16:18
comments: false

go-入坑筆記.
代碼實(shí)踐倉(cāng)庫(kù): https://github.com/yangxuan0261/GoLab


前篇

  • Go 入門指南 - http://wiki.jikexueyuan.com/project/the-way-to-go/
  • Go 語(yǔ)言圣經(jīng) - https://books.studygolang.com/gopl-zh/ch0/ch0-01.html
  • 對(duì)pkg里面的針對(duì)每個(gè)函數(shù)寫代碼例子 - https://github.com/astaxie/gopkg
  • 通用教程 - https://github.com/astaxie/build-web-application-with-golang
  • 由一個(gè)經(jīng)驗(yàn)豐富的Go程序員群體編寫的一系列Go學(xué)習(xí)范例 - https://github.com/mkaz/working-with-go/tree/master/pages
  • Go社區(qū)投票選舉出來(lái)的最好的在線 Go 教程 - https://hackr.io/tutorials/learn-golang
  • Go by Example - https://gobyexample.com/
  • 教程清單 - https://github.com/yinggaozhen/awesome-go-cn

環(huán)境配置 (廢棄, 使用 Goland 編碼體驗(yàn)更好)


windows vscode 配置 (2019.10.10)

  • 弄好代理, Proxifier 代理到 ssr. 這樣命令行才能走到 代理上

  • ctrl + shift + p, 輸入 go install, 選擇 go: install/update tools, 然后全選, 點(diǎn)擊ok

    只要代理弄好了, 全部都能安裝成功. 生成的 一些列 exe 會(huì)在 bin 目錄下

    Installing 1 tool at F:\a_link_workspace\go\GoWinEnv_MicroExamples\bingoimports...Installing golang.org/x/tools/cmd/goimports SUCCEEDED ...All tools successfully installed. You're ready to Go :).
  • src 下的項(xiàng)目用 模塊管理. 參考: [模塊依賴 go mod](#模塊依賴 go mod)


  • vscode 模塊化支持

    參考: Go modules support in Visual Studio Code - https://github.com/Microsoft/vscode-go/wiki/Go-modules-support-in-Visual-Studio-Code


    工程模板

    • https://github.com/yangxuan0261/GoWinEnv_Template

    新建一個(gè)項(xiàng)目

    micro 文件夾下的才是項(xiàng)目的源碼, 其他文件夾都是依賴的第三方庫(kù). 所以 micro 才是 git 倉(cāng)庫(kù).

    • windows 上使用 Goland 打開 GoEnvMicro 作為工程
    • Linux 中使用 docker 的話, 只要將 micro 路徑掛載進(jìn)去

    Goland 方式

  • 新建一個(gè) go 項(xiàng)目目錄 GoEnvMicro, 再建一個(gè)子目錄 src, src 目錄下的 micro 才是真正的項(xiàng)目源碼, 這個(gè)才需要用 git 版本控制的代碼, GitHub 里的 go 開源項(xiàng)目就是這個(gè). (pkg 目錄會(huì)自動(dòng)生成)

  • 用 Goland 打開 GoEnvMicro 這個(gè)目錄作為項(xiàng)目

  • 模塊化這個(gè)項(xiàng)目源碼目錄, cd 到 micro 目錄下使用命令: go mod initxxx

    E:\ws_go\GoEnvMicro\src\micro $ go mod init micro go: creating new go.mod: module micro
  • 此時(shí)就可以 run 和 build項(xiàng)目了, 但是 Goland 中提示會(huì)報(bào)錯(cuò), 且不能跳轉(zhuǎn)到 項(xiàng)目?jī)?nèi)/第三方庫(kù) 的符號(hào).

  • 項(xiàng)目條狀符號(hào), 添加項(xiàng)目 GOPATH (最好除一下系統(tǒng)的 GOPATH)

    import "micro/tool" // 就不會(huì)報(bào)錯(cuò)了
  • alt + enter 快捷鍵 go get 第三方庫(kù)

  • 然后就可以跳轉(zhuǎn)到 項(xiàng)目?jī)?nèi)/第三方庫(kù) 的符號(hào)了


    docker 方式

    docker 方式就簡(jiǎn)單很多

    只要將 micro 路徑掛載進(jìn)去即可, 然后進(jìn)入 golang docker 實(shí)例中 編譯/運(yùn)行. 參考: docker_golang使用.md


    Go 語(yǔ)言風(fēng)格指南

    • https://github.com/uber-go/guide/blob/master/style.md

    優(yōu)秀開源項(xiàng)目

    • https://zhuanlan.zhihu.com/p/50413709
    • https://github.com/hackstoic/golang-open-source-projects
    • https://github.com/yinggaozhen/awesome-go-cn (匯總了多個(gè)開源項(xiàng)目)

    vscode go.toolsGopath

    • https://github.com/Microsoft/vscode-go/wiki/GOPATH-in-the-VS-Code-Go-extension

    允許將 go get 下載到的工具隔離到一個(gè) go.toolsGopath 指定的目錄中


    go get 規(guī)則

    • https://tip.golang.org/cmd/go/#hdr-Module_queries

    For example, these commands are all valid:

    go get -d -v github.com/gorilla/mux@latest # same (@latest is default for 'go get') go get -d -v github.com/gorilla/mux@v1.6.2 # records v1.6.2 go get -d -v github.com/gorilla/mux@e3702bed2 # records v1.6.2 go get -d -v github.com/gorilla/mux@c856192 # records v0.0.0-20180517173623-c85619274f5d go get -d -v github.com/gorilla/mux@master # records current meaning of master
    • -d : 標(biāo)志只下載代碼包,不執(zhí)行安裝命令;
    • -v : 打印詳細(xì)日志和調(diào)試日志。這里加上這個(gè)標(biāo)志會(huì)把每個(gè)下載的包都打印出來(lái);

    基礎(chǔ)語(yǔ)法教程

    菜鳥教程: http://www.runoob.com/go/go-environment.html


    golang 多核調(diào)度

    參考:

    • 對(duì)golang多核編程的一點(diǎn)了解 - https://studygolang.com/articles/9686
    • Go語(yǔ)言中的多核調(diào)度 - https://blog.csdn.net/InsZVA/article/details/54081605
    • Golang 的 協(xié)程調(diào)度機(jī)制 與 GOMAXPROCS 性能調(diào)優(yōu) - https://juejin.im/post/5b7678f451882533110e8948

    GOMAXPROCS

    在 Go語(yǔ)言程序運(yùn)行時(shí)(runtime)實(shí)現(xiàn)了一個(gè)小型的任務(wù)調(diào)度器。這套調(diào)度器的工作原理類似于操作系統(tǒng)調(diào)度線程,Go 程序調(diào)度器可以高效地將 CPU 資源分配給每一個(gè)任務(wù)。傳統(tǒng)邏輯中,開發(fā)者需要維護(hù)線程池中線程與 CPU 核心數(shù)量的對(duì)應(yīng)關(guān)系。同樣的,Go 地中也可以通過(guò) runtime.GOMAXPROCS() 函數(shù)做到,格式為:

    runtime.GOMAXPROCS(邏輯CPU數(shù)量)

    這里的邏輯CPU數(shù)量可以有如下幾種數(shù)值:

    • <1:不修改任何數(shù)值。
    • =1:單核心執(zhí)行。
    • >1:多核并發(fā)執(zhí)行。

    一般情況下,可以使用 runtime.NumCPU() 查詢 CPU 數(shù)量,并使用 runtime.GOMAXPROCS() 函數(shù)進(jìn)行設(shè)置,例如:

    runtime.GOMAXPROCS(runtime.NumCPU())

    Go 1.5 版本之前,默認(rèn)使用的是單核心執(zhí)行。從 Go 1.5 版本開始,默認(rèn)執(zhí)行上面語(yǔ)句以便讓代碼并發(fā)執(zhí)行,最大效率地利用 CPU。

    GOMAXPROCS 同時(shí)也是一個(gè)環(huán)境變量,在應(yīng)用程序啟動(dòng)前設(shè)置環(huán)境變量也可以起到相同的作用。


    常用庫(kù)

    • 官網(wǎng)包 - https://godoc.org/
    • Golang常用包有哪些?- https://www.zhihu.com/question/22009370

    編碼規(guī)范

    • https://gocn.vip/article/1

    構(gòu)建, 打包, 執(zhí)行

    go build

    通過(guò)go build加上要編譯的Go源文件名,我們即可得到一個(gè)可執(zhí)行文件,默認(rèn)情況下這個(gè)文件的名字為源文件名字去掉.go后綴。

    $ go build hellogo.go $ ls hellogo* hellogo.go

    當(dāng)然我們也 可以通過(guò)-o選項(xiàng)來(lái)指定其他名字 myfirstgo (這個(gè)可執(zhí)行文件):

    $ go build -o myfirstgo hellogo.go $ ls myfirstgo* hellogo.go
    • 如果是window平臺(tái), 則要導(dǎo)出 xxx.exe 可執(zhí)行文件

      $ go build -o myfirstgo.exe hellogo.go

    如果我們?cè)趃o-examples目錄下直接執(zhí)行g(shù)o build命令,后面不帶文件名,我們將得到一個(gè)與目錄名同名的可執(zhí)行文件:

    $ go build $ ls go-examples* hellogo.go
    • 指定導(dǎo)出 %GOPATH%/src 下某個(gè)文件夾下的程序. 該文件加下的必須有 go文件是有 package main 和 func main()

      E:\GoWinEnv>go build -o hello.exe GoLab/test_file
      • 生成的 hello.exe 在執(zhí)行命令的該文件夾下, 這里就是在 E:\GoWinEnv

    go install

    與build命令相比,install命令在編譯源碼后還會(huì)將可執(zhí)行文件或庫(kù)文件安裝到約定的目錄下。

    • go install編譯出的可執(zhí)行文件以其所在目錄名(DIR)命名
    • go install將可執(zhí)行文件安裝到與src同級(jí)別的bin目錄下,bin目錄由go install自動(dòng)創(chuàng)建
    • go install將可執(zhí)行文件依賴的各種package編譯后,放在與src同級(jí)別的pkg目錄下.

    go run

    直接執(zhí)行某個(gè) go 文件

    $ cd src\GoLab\test_grpc\greeter_client $ go run test_grpc_cli.go

    參考資料:

    • http://tonybai.com/2012/08/17/hello-go/

    面向?qū)ο缶幊?/h3>
    • https://blog.csdn.net/zhangjg_blog/article/details/18790965

    package

    • 同一個(gè)目錄下不能存在不同包名的文件

    • import 別的包規(guī)則

      package mainimport (pkg001 "GoLab/test_pkg/pkg001" // 重命名別名為 pkg001 在本文件中的使用, 一般不要這樣干_ "GoLab/test_pkg/pkg002" // _ 防止 未被使用的包, 被格式化代碼時(shí)被編輯器自動(dòng)干掉這一行"fmt" // 導(dǎo)入內(nèi)置包 )
      • 包名路徑: 是 GOPATH/src 為基礎(chǔ)搜索.
    • import 的流程. 參考: https://blog.csdn.net/zhangzhebjut/article/details/25564457

      程序的初始化和執(zhí)行都起始于main包。如果main包還導(dǎo)入了其它的包,那么就會(huì)在編譯時(shí)將它們依次導(dǎo)入。有時(shí)一個(gè)包會(huì)被多個(gè)包同時(shí)導(dǎo)入,那么它只會(huì)被導(dǎo)入一次(例如很多包可能都會(huì)用到fmt包,但它只會(huì)被導(dǎo)入一次,因?yàn)闆]有必要導(dǎo)入多次)。當(dāng)一個(gè)包被導(dǎo)入時(shí),如果該包還導(dǎo)入了其它的包,那么會(huì)先將其它包導(dǎo)入進(jìn)來(lái),然后再對(duì)這些包中的包級(jí)常量和變量進(jìn)行初始化,接著執(zhí)行init函數(shù)(如果有的話),依次類推。等所有被導(dǎo)入的包都加載完畢了,就會(huì)開始對(duì)main包中的包級(jí)常量和變量進(jìn)行初始化,然后執(zhí)行main包中的init函數(shù)(如果存在的話),最后執(zhí)行main函數(shù)。下圖詳細(xì)地解釋了整個(gè)執(zhí)行過(guò)程:

    • import

    • var

    • init()

    • main()

    • func init() { // 是保留的內(nèi)置方法, import時(shí)自動(dòng)執(zhí)行fmt.Println("--- init") }

    空結(jié)構(gòu)體

    空結(jié)構(gòu)體的特點(diǎn):1、不占用內(nèi)存;2、地址不變

    map 的 value, 變相為 set

    empty := struct{}{}println("empty len:", unsafe.Sizeof(empty)) // 0, 空結(jié)構(gòu)體的長(zhǎng)度為 0, 常用于 map 里面做 value 值, 因?yàn)?go 里面集合沒有 set, 所以用 map 變相做 set

    協(xié)程的型號(hào)量

    var ch chan struct{}work := func() {log.Println("--- work")time.Sleep(time.Second * 3)<-ch }ch = make(chan struct{}, 10) for i := 0; i < 15; i++ {ch <- struct{}{}go work() }

    Go 關(guān)鍵字和 channel 的用法

    make

    golang分配內(nèi)存有一個(gè)make函數(shù),該函數(shù)第一個(gè)參數(shù)是類型,第二個(gè)參數(shù)是分配的空間,第三個(gè)參數(shù)是預(yù)留分配空間. 例如a:=make([]int, 5, 10), len(a)輸出結(jié)果是5,cap(a)輸出結(jié)果是10,然后對(duì)a[4]進(jìn)行賦值發(fā)現(xiàn)是可以得,但對(duì)a[5]進(jìn)行賦值發(fā)現(xiàn)報(bào)錯(cuò)了,于是郁悶這個(gè)預(yù)留分配的空間要怎么使用呢,于是google了一下發(fā)現(xiàn)原來(lái)預(yù)留的空間需要重新切片才可以使用,于是做一下記錄,代碼如下。

    func main(){a := make([]int, 10, 20)fmt.Printf("%d, %d\n", len(a), cap(a))fmt.Println(a)b := a[:cap(a)]fmt.Println(b) } /* 10, 20 [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] */

    make()分配:內(nèi)部函數(shù) make(T, args) 的服務(wù)目的和 new(T) 不同。
    它只生成切片,映射和程道,并返回一個(gè)初始化的(不是零)的,type T的,不是 *T 的值。
    這種區(qū)分的原因是,這三種類型的數(shù)據(jù)結(jié)構(gòu)必須在使用前初始化.
    比如切片是一個(gè)三項(xiàng)的描述符,包含數(shù)據(jù)指針(數(shù)組內(nèi)),長(zhǎng)度,和容量;在這些項(xiàng)初始化前,切片為 nil 。
    對(duì)于切片、映射和程道,make初始化內(nèi)部數(shù)據(jù)結(jié)構(gòu),并準(zhǔn)備要用的值。
    記住 make() 只用于 映射、切片、程道,不返回指針。要明確的得到指針用 new() 分配。

    go 關(guān)鍵字用來(lái)創(chuàng)建 goroutine (協(xié)程),是實(shí)現(xiàn)并發(fā)的關(guān)鍵。go 關(guān)鍵字的用法如下:

    //go 關(guān)鍵字放在方法調(diào)用前新建一個(gè) goroutine 并讓他執(zhí)行方法體 go GetThingDone(param1, param2);//上例的變種,新建一個(gè)匿名方法并執(zhí)行 go func(param1, param2) { }(val1, val2)//直接新建一個(gè) goroutine 并在 goroutine 中執(zhí)行代碼塊 go {//do someting... }

    因?yàn)?goroutine 在多核 cpu 環(huán)境下是并行的。如果代碼塊在多個(gè) goroutine 中執(zhí)行,我們就實(shí)現(xiàn)了代碼并行。那么問(wèn)題來(lái)了,怎么拿到并行的結(jié)果呢?這就得用 channel 了。

    //resultChan 是一個(gè) int 類型的 channel。類似一個(gè)信封,里面放的是 int 類型的值。 var resultChan chan int //將 123 放到這個(gè)信封里面,供別人從信封中取用 resultChan <- 123 //從 resultChan 中取值。這個(gè)時(shí)候 result := 123 result := <- resultChan

    channel 詳解

    chan 是信號(hào)的關(guān)鍵字, 作用有點(diǎn)像c++多線程里面的 signal, 發(fā)送信號(hào)給其他線程, 通知其可以繼續(xù)往下跑了.
    在 go 里 chan 的使用是結(jié)合了 go,select 實(shí)現(xiàn)了 goroutine, 多并發(fā).

    func test_chan03() {c1 := make(chan string) // 聲明 信號(hào)c2 := make(chan string)go func() { // go 關(guān)鍵字, 新建一個(gè)協(xié)程跑這個(gè)方法time.Sleep(time.Second * 1)c1 <- "one" // 往 c1 信號(hào)中丟數(shù)據(jù), 也就是通知 c1 阻塞的地方可以繼續(xù)跑了}()go func() {time.Sleep(time.Second * 2)c2 <- "two"}()for i := 0; i < 2; i++ {select {case msg1 := <-c1: // 阻塞, 等待 c1 信號(hào)通知, 如果收到通知, 這跑這個(gè)case, 并把數(shù)據(jù)丟該 msg1fmt.Println("received", msg1)case msg2 := <-c2:fmt.Println("received", msg2)}}fmt.Println("程序結(jié)束666")/*msg1 := <-c1 表示 阻塞, 等待c1信號(hào)通知, 收到通知后把數(shù)據(jù) 賦值給 msg1. 如果不需要信號(hào)中的數(shù)據(jù), 可以可以這樣寫 <-c1c1 <- "one" 表示 通知 c1 信號(hào)阻塞的地方可以繼續(xù)運(yùn)行了, 并往里面丟了一個(gè)數(shù)據(jù) "one"*/ }

    channel 讀寫加持

    chan 在函數(shù)中定義形參是時(shí)可以指定是 讀寫,只讀,只寫 三個(gè)形式, 作用與 c++ 中 const關(guān)鍵字 差不多

    fnRW := func(c chan int) { // c可以讀寫c <- 6val := <-cfmt.Println("val:", val) }fnR := func(c <-chan int) { // c只讀// c <- 6 // 報(bào)錯(cuò): send to receive-only type <-chan intval := <-cfmt.Println("val:", val) }fnW := func(c chan<- int) { // c只寫// <-c // 報(bào)錯(cuò): receive from send-only type chan<- intc <- 6 }

    參考: https://www.cnblogs.com/baiyuxiong/p/4545028.html

    channel 賦值不生效

    可能沒有初始化變量 make(chan string)


    defer

    defer 的思想類似于C++中的析構(gòu)函數(shù),不過(guò)Go語(yǔ)言中“析構(gòu)”的不是對(duì)象,而是函數(shù),defer就是用來(lái)添加函數(shù)結(jié)束時(shí)執(zhí)行的語(yǔ)句。注意這里強(qiáng)調(diào)的是添加,而不是指定,因?yàn)椴煌贑++中的析構(gòu)函數(shù)是靜態(tài)的,Go中的defer是動(dòng)態(tài)的
    defer 中使用匿名函數(shù)依然是一個(gè)閉包。

    func test_defer() {x, y := 1, 2defer func(a int) {fmt.Printf("x:%d,y:%d\n", a, y) // y 為閉包引用}(x) // 復(fù)制 x 的值x += 100y += 100fmt.Println(x, y) }

    defer 還有一個(gè)重要的作用是用于 panic 時(shí)的 恢復(fù), panic 恢復(fù)也只能在 defer 中.
    參考: http://wiki.jikexueyuan.com/project/the-way-to-go/13.3.html

    參考: 5 年 Gopher 都不知道的 defer 細(xì)節(jié),你別再掉進(jìn)坑里

    什么是 defer

    defer 是 Go 語(yǔ)言提供的一種用于注冊(cè)延遲調(diào)用的機(jī)制,每一次 defer 都會(huì)把函數(shù)壓入棧中,當(dāng)前函數(shù)返回前再把延遲函數(shù)取出并執(zhí)行。

    defer 語(yǔ)句并不會(huì)馬上執(zhí)行,而是會(huì)進(jìn)入一個(gè)棧,函數(shù) return 前,會(huì)按先進(jìn)后出(FILO)的順序執(zhí)行。也就是說(shuō)最先被定義的 defer 語(yǔ)句最后執(zhí)行。先進(jìn)后出的原因是后面定義的函數(shù)可能會(huì)依賴前面的資源,自然要先執(zhí)行;否則,如果前面先執(zhí)行,那后面函數(shù)的依賴就沒有了

    采坑點(diǎn)

    使用 defer 最容易采坑的地方是和帶命名返回參數(shù)的函數(shù)一起使用時(shí)。

    defer 語(yǔ)句定義時(shí),對(duì)外部變量的引用是有兩種方式的,分別是作為函數(shù)參數(shù)和作為閉包引用。作為函數(shù)參數(shù),則在 defer 定義時(shí)就把值傳遞給 defer,并被緩存起來(lái);作為閉包引用的話,則會(huì)在 defer 函數(shù)真正調(diào)用時(shí)根據(jù)整個(gè)上下文確定當(dāng)前的值。

    避免掉坑的關(guān)鍵是要理解這條語(yǔ)句:

    return xxx

    這條語(yǔ)句并不是一個(gè)原子指令,經(jīng)過(guò)編譯之后,變成了三條指令:

    1.返回值=xxx 2.調(diào)用defer函數(shù) 3.空的return

    1,3 步才是 return 語(yǔ)句真正的命令,第 2 步是 defer 定義的語(yǔ)句,這里就有可能會(huì)操作返回值


    性能

    • Go defer 會(huì)有性能損耗,盡量不要用?- https://segmentfault.com/a/1190000019490834

    補(bǔ)充上柴大的回復(fù):“不是性能問(wèn)題,defer 最大的功能是 Panic 后依然有效。如果沒有 defer,Panic 后就會(huì)導(dǎo)致 unlock 丟失,從而導(dǎo)致死鎖了”,非常經(jīng)典。

    結(jié)論, 性能影響小, 但對(duì)于 調(diào)用極多 的函數(shù), 能不用就不用.


    new make 區(qū)別

    • new

      new 這個(gè)內(nèi)置函數(shù),可以給我們分配一塊內(nèi)存讓我們使用,但是現(xiàn)實(shí)的編碼中,它是不常用的。我們通常都是采用短語(yǔ)句聲明以及結(jié)構(gòu)體的字面量達(dá)到我們的目的,比如: u:=&user{}

    • make

      make 函數(shù)是無(wú)可替代的,我們?cè)谑褂?slice、map 以及 channel 的時(shí)候,還是要使用 make 進(jìn)行初始化,然后才才可以對(duì)他們進(jìn)行操作。

    make 返回的還是這三個(gè)引用類型本身;而 new 返回的是指向類型的指針。


    CGO

    • https://studygolang.com/articles/16812

    CGO 提供了 golang 和 C 語(yǔ)言相互調(diào)用的機(jī)制。某些第三方庫(kù)可能只有 C/C++ 的實(shí)現(xiàn),完全用純 golang 的實(shí)現(xiàn)可能工程浩大,這時(shí)候 CGO 就派上用場(chǎng)了。可以通 CGO 在 golang 在調(diào)用 C 的接口,C++ 的接口可以用 C 包裝一下提供給 golang 調(diào)用。被調(diào)用的 C 代碼可以直接以源代碼形式提供或者打包靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)在編譯時(shí)鏈接。推薦使用靜態(tài)庫(kù)的方式,這樣方便代碼隔離,編譯的二進(jìn)制也沒有動(dòng)態(tài)庫(kù)依賴方便發(fā)布也符合 golang 的哲學(xué)。

    goroutine 通過(guò) CGO 進(jìn)入到 C 接口的執(zhí)行階段后,已經(jīng)脫離了 golang 運(yùn)行時(shí)的調(diào)度并且會(huì)獨(dú)占線程,此時(shí)實(shí)際上變成了多線程同步的編程模型。如果 C 接口里有阻塞操作,這時(shí)候可能會(huì)導(dǎo)致所有線程都處于阻塞狀態(tài),其他 goroutine 沒有機(jī)會(huì)得到調(diào)度,最終導(dǎo)致整個(gè)系統(tǒng)的性能大大較低。總的來(lái)說(shuō),只有在第三方庫(kù)沒有 golang 的實(shí)現(xiàn)并且實(shí)現(xiàn)起來(lái)成本比較高的情況下才需要考慮使用 CGO ,否則慎用。


    熱重啟

    • Golang中的熱重啟 - https://cloud.tencent.com/developer/article/1388556

    Go 錯(cuò)誤處理

    Go 語(yǔ)言通過(guò)內(nèi)置的錯(cuò)誤接口提供了非常簡(jiǎn)單的錯(cuò)誤處理機(jī)制。
    error類型是一個(gè)接口類型,這是它的定義:

    type error interface {Error() string }

    我們可以在編碼中通過(guò)實(shí)現(xiàn) error 接口類型來(lái)生成錯(cuò)誤信息。
    函數(shù)通常在最后的返回值中返回錯(cuò)誤信息。使用errors.New 可返回一個(gè)錯(cuò)誤信息:

    func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math: square root of negative number")}// 實(shí)現(xiàn) }

    在下面的例子中,我們?cè)谡{(diào)用Sqrt的時(shí)候傳遞的一個(gè)負(fù)數(shù),然后就得到了non-nil的error對(duì)象,將此對(duì)象與nil比較,結(jié)果為true,所以fmt.Println(fmt包在處理error時(shí)會(huì)調(diào)用Error方法)被調(diào)用,以輸出錯(cuò)誤,請(qǐng)看下面調(diào)用的示例代碼:

    result, err:= Sqrt(-1)if err != nil {fmt.Println(err) }

    select 語(yǔ)句的行為

    // https://talks.golang.org/2012/concurrency.slide#32 select { case v1 := <-c1:fmt.Printf("received %v from c1\n", v1) case v2 := <-c2:fmt.Printf("received %v from c2\n", v1) case c3 <- 23:fmt.Printf("sent %v to c3\n", 23) default:fmt.Printf("no one was ready to communicate\n") }

    上面這段代碼中,select 語(yǔ)句有四個(gè) case 子語(yǔ)句,前兩個(gè)是 receive 操作,第三個(gè)是 send 操作,最后一個(gè)是默認(rèn)操作。代碼執(zhí)行到 select 時(shí),case 語(yǔ)句會(huì)按照源代碼的順序被評(píng)估,且只評(píng)估一次,評(píng)估的結(jié)果會(huì)出現(xiàn)下面這幾種情況:

  • 除 default 外,如果只有一個(gè) case 語(yǔ)句評(píng)估通過(guò),那么就執(zhí)行這個(gè)case里的語(yǔ)句;
  • 除 default 外,如果有多個(gè) case 語(yǔ)句評(píng)估通過(guò),那么通過(guò)偽隨機(jī)的方式隨機(jī)選一個(gè);
  • 如果 default 外的 case 語(yǔ)句都沒有通過(guò)評(píng)估,那么執(zhí)行 default 里的語(yǔ)句;
  • 如果沒有 default,那么 代碼塊會(huì)被阻塞,指導(dǎo)有一個(gè) case 通過(guò)評(píng)估;否則一直阻塞
  • 以下描述了 select 語(yǔ)句的語(yǔ)法:

    • 每個(gè)case都必須是一個(gè)通信

    • 所有channel表達(dá)式都會(huì)被求值

    • 所有被發(fā)送的表達(dá)式都會(huì)被求值

    • 如果任意某個(gè)通信可以進(jìn)行,它就執(zhí)行;其他被忽略。

    • 如果有多個(gè)case都可以運(yùn)行,Select會(huì)隨機(jī)公平地選出一個(gè)執(zhí)行。其他不會(huì)執(zhí)行。

      否則:

      • 如果有default子句,則執(zhí)行該語(yǔ)句。
      • 如果沒有default字句,select將阻塞,直到某個(gè)通信可以運(yùn)行;Go不會(huì)重新對(duì)channel或值進(jìn)行求值。

    接口 interface

    Go 語(yǔ)言提供了另外一種數(shù)據(jù)類型即接口,它把所有的具有共性的方法定義在一起,任何其他類型只要實(shí)現(xiàn)了這些方法就是實(shí)現(xiàn)了這個(gè)接口。

    /* 定義接口 */ type interface_name interface {method_name1 [return_type]method_name2 [return_type]method_name3 [return_type]...method_namen [return_type] }/* 定義結(jié)構(gòu)體 */ type struct_name struct {/* variables */ }/* 實(shí)現(xiàn)接口方法 */ func (struct_name_variable struct_name) method_name1() [return_type] {/* 方法實(shí)現(xiàn) */ } ... func (struct_name_variable struct_name) method_namen() [return_type] {/* 方法實(shí)現(xiàn)*/ }

    什么時(shí)候需要指針 *

    當(dāng)使用 interface 時(shí)不需要 *, 只有 struct 時(shí)才需要 *

    type CDog struct { }func (this *CDog) Run() { // 實(shí)現(xiàn) IDog 的所有接口fmt.Println("--- run") }type IDog interface{Run() }var ptr1 interface{} ptr1 = &CDog{} // interface 變量接收的是指針, 而不是對(duì)象 if myDog, ok := ptr1.(*CDog); ok { // 動(dòng)態(tài)匹配 CDog 指針fmt.Printf("--- ptr1 is CDog \n") // --- ptr1 is CDog }if myDog, ok := ptr1.(IDog); ok { // 動(dòng)態(tài)匹配 IDog 接口, 不需要指針?lè)?hào) *fmt.Printf("--- ptr1 is IDog \n") // --- ptr1 is CDog }

    context

    • Go語(yǔ)言實(shí)戰(zhàn)筆記(二十)| Go Context - https://www.flysnow.org/2017/05/12/go-in-action-go-context.html
    • https://juejin.im/post/5a6873fef265da3e317e55b6

    一個(gè)是Background,主要用于main函數(shù)、初始化以及測(cè)試代碼中,作為Context這個(gè)樹結(jié)構(gòu)的最頂層的Context,也就是根Context。

    一個(gè)是TODO,它目前還不知道具體的使用場(chǎng)景,如果我們不知道該使用什么Context的時(shí)候,可以使用這個(gè)。

    他們兩個(gè)本質(zhì)上都是emptyCtx結(jié)構(gòu)體類型,是一個(gè)不可取消,沒有設(shè)置截止時(shí)間,沒有攜帶任何值的Context。

    • 創(chuàng)建 ctx 的四個(gè)接口

      func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context

      這四個(gè)With函數(shù),接收的都有一個(gè)partent參數(shù),就是父Context,我們要基于這個(gè)父Context創(chuàng)建出子Context的意思,這種方式可以理解為子Context對(duì)父Context的繼承,也可以理解為基于父Context的衍生。

      通過(guò)這些函數(shù),就創(chuàng)建了一顆Context樹,樹的每個(gè)節(jié)點(diǎn)都可以有任意多個(gè)子節(jié)點(diǎn),節(jié)點(diǎn)層級(jí)可以有任意多個(gè)。

      • WithCancel 函數(shù),傳遞一個(gè)父 Context作為參數(shù),返回子Context,以及一個(gè)取消函數(shù)用來(lái)取消Context。 WithDeadline 函數(shù),和 WithCancel 差不多,它會(huì)多傳遞一個(gè)截止時(shí)間參數(shù),意味著到了這個(gè)時(shí)間點(diǎn),會(huì)自動(dòng)取消 Context,當(dāng)然我們也可以不等到這個(gè)時(shí)候,可以提前通過(guò)取消函數(shù)進(jìn)行取消。

      • WithTimeout 和 WithDeadline基本上一樣,一個(gè)是 多少時(shí)間后 超時(shí)自動(dòng)取消,一個(gè)是 什么時(shí)間點(diǎn) 超時(shí)自動(dòng)取消。

      • WithValue 函數(shù)和取消Context無(wú)關(guān),它是為了生成一個(gè)綁定了一個(gè)鍵值對(duì)數(shù)據(jù)的 Context,這個(gè)綁定的數(shù)據(jù)可以通過(guò) Context.Value 方法訪問(wèn)到


    protobuf 使用

    proto3

    • https://juejin.im/post/5bb597c2e51d450e6e03e42d

    參考: https://blog.csdn.net/wangshubo1989/article/details/72478014

    報(bào)錯(cuò): 語(yǔ)法沒有指定
    -> 在 test.proto 指定語(yǔ)法

    syntax = "proto2"; package goprotobuf;

    報(bào)錯(cuò): --go_out: protoc-gen-go: 系統(tǒng)找不到指定的文件
    -> 需要將 go get -u github.com/golang/protobuf/protoc-gen-go 下載的 protoc-gen-go.exe 所在目錄加入環(huán)境變量

    安裝

  • 下載 protoc. https://github.com/protocolbuffers/protobuf/releases

  • 下載并生成 protoc-gen-go.exe, protoc-gen-micro.exe 等 Golang for protobuf 插件

    $ go get -u -v github.com/golang/protobuf/proto $ go get -u -v github.com/golang/protobuf/protoc-gen-go
  • 生成 grpc xxx.pb.go

    得先安裝 grpc. 參考: grpc

    • 執(zhí)行命令所在路徑為 test_grpc

      ├─test_grpc │ ├─aaa │ ├─greeter_client │ ├─greeter_server │ └─protos // 所有 .proto 文件

      執(zhí)行命令

      protoc --go_out=plugins=grpc:aaa -I .\protos ./protos/helloworld.proto
      • -I .\protos : 指定 依賴路徑, 因?yàn)?proto 文件在同一文件夾內(nèi)必須是相同 package (相同模塊), 如果依賴了其他模塊需要指定 -I 參數(shù)指定搜索路徑

        • 奇怪的是帶上 -I 參數(shù), 則會(huì)在輸出目錄 aaa 下直接生成 xxx.pb.go, 不帶 -I 參數(shù)則會(huì)生成多一級(jí)目錄, 也就是 aaa/protos/xxx.pb.go
      • .\protos\helloworld.proto : 指定要生成的文件為 protos\helloworld.proto 文件

        • 如果該模塊有多個(gè) proto 文件需要轉(zhuǎn)換的話, 可以使用 ./protos/*.proto, 會(huì)匹配生成多個(gè) xxx.pb.go 文件
        • 也可以分別制定多個(gè) proto 文件, 用空格隔開. ./protos/aaa.proto ./protos/bbb.proto
      • --go_out=plugins=grpc:aaa : 輸出文件 到 aaa (必須已經(jīng)存在) 目錄下, 格式為 go 語(yǔ)言的 grpc 格式

        • 如果生成普通的 xxx.pb.go 的話, 使用 protoc-gen-go, 則為: --go_out=protoc-gen-go:bbb
      • 結(jié)果: 會(huì)在 aaa 文件夾下生成 xxx.pb.go 文件

    生成 普通 xxx.pb.go

    • 執(zhí)行命令. ( protoc-gen-go.exe 已經(jīng)在環(huán)境變量中, 且輸出目錄 bbb 已經(jīng)存在 )

      protoc -I .\protos\ --go_out=protoc-gen-go:bbb ./protos/helloworld.proto

    插件 gofast, 高性能

    • golang使用protobuf - https://segmentfault.com/a/1190000009277748

    gogoprotobuf有兩個(gè)插件可以使用

    • protoc-gen-gogo:和protoc-gen-go生成的文件差不多,性能也幾乎一樣(稍微快一點(diǎn)點(diǎn))
    • protoc-gen-gofast:生成的文件更復(fù)雜,性能也更高(快5-7倍)
    //gogo $ go get github.com/gogo/protobuf/protoc-gen-gogo $ protoc --gogo_out=. *.proto//gofast $ go get github.com/gogo/protobuf/protoc-gen-gofast $ protoc --gofast_out=. *.proto

    proto2 語(yǔ)法生成字段都是指針

    • https://github.com/golang/protobuf/issues/22

    grpc

    • grpc應(yīng)用詳解 - https://www.jishuwen.com/d/2BV4
    • Go實(shí)踐微服務(wù) – gRPC配置和使用 - https://yuanxuxu.com/2018/06/20/go-microservice-in-action-grpc/
    • gRPC 基礎(chǔ): Go - https://doc.oschina.net/grpc?t=60133
    • https://github.com/micro/development/blob/master/clients.md

    使用的 proto 的語(yǔ)法必須是3. ( syntax = "proto3"; )


    安裝

    go get -u -v google.golang.org/grpc

    生成包含 grpc 的 pb

    • Go實(shí)戰(zhàn)–golang中使用gRPC和Protobuf實(shí)現(xiàn)高性能api(golang/protobuf、google.golang.org/grpc) - https://blog.csdn.net/wangshubo1989/article/details/78739994
    $ protoc --go_out=plugins=grpc:. your_pb.proto

    單向調(diào)用

    也就是 請(qǐng)求->響應(yīng) 式的調(diào)用.

    參考: grpc_call

    // proto syntax = "proto3";package helloworld;service Greeter{rpc SayHello (HelloRequest) returns (HelloReply){} }message HelloRequest{string name = 1; }message HelloReply{string message = 1; }// go func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {fmt.Printf("--- from cli, say:%s\n", in.Name)return &pb.HelloReply{Message: "hello " + in.Name}, nil }

    雙向調(diào)用

    雙方都可主動(dòng)推送流過(guò)去

    參考: grpc_stream_putget

    // proto syntax = "proto3";//聲明proto的版本 只能 是3,才支持 grpcpackage pro;service Greeter {rpc AllStream (stream StreamReqData) returns (stream StreamResData){} }message StreamReqData {string data = 1; }message StreamResData {string data = 1; }// go //客戶端服務(wù)端 雙向流, req res 都定義了 stream, 必須通過(guò) recv 和 send 接收發(fā)送數(shù)據(jù), 然后再通過(guò) .xxx 獲取屬性值 func (this *server) AllStream(allStr pro.Greeter_AllStreamServer) error {wg := sync.WaitGroup{}wg.Add(2)go func() {for {data, _ := allStr.Recv()log.Println("--- srv AllStream recv:", data.Data)}wg.Done()}()go func() {for {allStr.Send(&pro.StreamResData{Data: "--- srv data"})time.Sleep(time.Second)}wg.Done()}()wg.Wait()return nil }

    TLS

    • 帶入gRPC:基于 CA 的 TLS 證書認(rèn)證 - https://studygolang.com/articles/15331

    模塊依賴 go mod

    • Go依賴模塊版本之Module避坑使用詳解 - https://www.cnblogs.com/sunsky303/p/10710637.html
    • go mod 使用 - https://juejin.im/post/5c8e503a6fb9a070d878184a
    • Go Modules 詳解使用 - https://learnku.com/articles/27401
    • Golang Modules ( 建議, 詳細(xì) ) - https://leileiluoluo.com/posts/golang-modules.html

    自從 Go 官方從去年推出 1.11 之后,增加新的依賴管理模塊并且更加易于管理項(xiàng)目中所需要的模塊。模塊是存儲(chǔ)在文件樹中的 Go 包的集合,其根目錄中包含 go.mod 文件。 go.mod 文件定義了模塊的模塊路徑,它也是用于根目錄的導(dǎo)入路徑,以及它的依賴性要求。每個(gè)依賴性要求都被寫為模塊路徑和特定語(yǔ)義版本。

    從 Go 1.11 開始,Go 允許在 $GOPATH/src 外的任何目錄下使用 go.mod 創(chuàng)建項(xiàng)目。在 $GOPATH/src 中,為了兼容性,Go 命令仍然在舊的 GOPATH 模式下運(yùn)行。從 Go 1.13 開始,模塊模式將成為默認(rèn)模式。

  • 環(huán)境變量設(shè)置

    export GO113MODULE=on // 113 是對(duì)應(yīng) go 的版本 1.13 export GOPROXY=https://goproxy.io // 設(shè)置代理 // 可以翻墻的話就不用設(shè)置這個(gè)代理了
  • 初始化新模塊.

  • 在 $GOPATH/src 之外的任何地方創(chuàng)建一個(gè)新的目錄

    F:\a_link_workspace\go\GoWinEnv_Test01\src (master -> origin) λ mkdir backend && cd backend
  • 模塊初始化. 命令: go mod init xxx. xxx 為模塊名 ( 模塊名最好和倉(cāng)庫(kù)根目錄路徑一致 ) . 會(huì)生成一個(gè)模塊配置文件 go.mod

    F:\a_link_workspace\go\GoWinEnv_Test01\src\backend (master -> origin) λ go mod init backend go: creating new go.mod: module backend
    • 查看內(nèi)容

      F:\a_link_workspace\go\GoWinEnv_Test01\src\backend (master -> origin) λ cat go.mod module backendgo 1.13
  • 測(cè)試. 新建一個(gè) main.go, 引入一個(gè)第三方模塊

    package mainimport "github.com/gin-gonic/gin" // 引入的第三方模塊func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // listen and serve on 0.0.0.0:8080 }
    • run 一下

      F:\a_link_workspace\go\GoWinEnv_Test01\src\backend (master -> origin) λ go run main.go

      go.mod 會(huì)自動(dòng)引入的第三方模塊,

      module backendgo 1.13require (github.com/gin-gonic/gin v1.4.0github.com/labstack/echo v3.3.10+incompatible // indirect )

  • 命令

    • go mod download 下載模塊到本地緩存,緩存路徑是 $GOPATH/pkg/mod/cache

    • go mod edit 是提供了命令版編輯 go.mod 的功能,例如 go mod edit -fmt go.mod 會(huì)格式化 go.mod

    • go mod graph 把模塊之間的依賴圖顯示出來(lái)

    • go mod init 初始化模塊(例如把原本dep管理的依賴關(guān)系轉(zhuǎn)換過(guò)來(lái))

    • go mod tidy 增加缺失的包,移除沒用的包

    • go mod vendor 把依賴拷貝到 vendor/ 目錄下

      修改 vendor 第三方庫(kù)使其生效. 參考: 使用本地包

      把第三方庫(kù)指到 vendor 目錄下對(duì)應(yīng)的庫(kù)即可

    • go mod verify 確認(rèn)依賴關(guān)系

    • go mod why 解釋為什么需要包和模塊


    使用本地包

    • go module使用本地包 - https://segmentfault.com/a/1190000018672890

    有些情況下需要修改第三方庫(kù)進(jìn)行 調(diào)試/打log, 不引用遠(yuǎn)程包

    直接修改 go.mod 文件, 修改 go-plugins 包的指向

    require (github.com/micro/go-plugins v1.3.0... )replace github.com/micro/go-plugins => ../github.com/micro/go-plugins // 修改為本地包, 可以使用相對(duì)于 go.mod 的相對(duì)路徑, 也可以使用絕對(duì)路徑

    然后修改本的 go-plugins 中的代碼就可以編譯進(jìn)去了.

    使用 本地包 有個(gè)坑. 因?yàn)?mod 模式下引用的包都是按需下載, 所以下載的第三方庫(kù)并不是所在完整的庫(kù). 一旦代碼引用到這個(gè)庫(kù)里面的其他 package 時(shí), 就會(huì)報(bào)找不到的錯(cuò)誤.

    go: github.com/micro/go-micro@v1.11.1: parsing ..\..\..\..\vendor\github.com\micro\go-micro\go.mod: open f:\a_link_workspace\go\GoWinEnv_new\src\vendor\github.com\micro\go-micro\go.mod: The system cannot find the path specified.

    解決辦法是 取消 掉 go.mod 中 指向本地包的代碼

    // replace github.com/micro/go-micro => ../vendor/github.com/micro/go-micro

    然后在 go run 一下, 就下載, 再 go mod vendor 一下會(huì)移到 vendor 目錄下

    f:\a_link_workspace\go\GoWinEnv_new\src\GoMicro\test_cmd\test_cmd_service\cmd1 (master -> origin) $ go run main.go $ go mod vendor
    正確的姿勢(shì)
  • 倉(cāng)庫(kù)根目錄下, 添加一個(gè) 子模塊, 將 完整的包 下下來(lái)丟到 src 目錄下

    • 如果不需要提交這個(gè) 子模塊, 在 .gitmodules 中刪掉這個(gè) 子模塊
  • 修改 go.mod 中倉(cāng)庫(kù)的 引用包 的指向

    replace github.com/micro/go-micro => ../github.com/micro/go-micro
  • 這樣修改 …/github.com/micro/go-micro 目錄下的代碼就能編譯進(jìn)去生效了.


  • 使用其他版本的包

    replace github.com/gogo/protobuf v0.0.0-20190410021324-65acae22fc9 => github.com/gogo/protobuf v0.0.0-20190723190241-65acae22fc9d // 指向其他版本的包require (github.com/golang/protobuf v1.3.2 )

    導(dǎo)入所有依賴

    • https://www.cnblogs.com/landv/p/10948227.html

    使用命令 go get -d -v ./...

    • -d : 標(biāo)志只下載代碼包,不執(zhí)行安裝命令;
    • -v : 打印詳細(xì)日志和調(diào)試日志。這里加上這個(gè)標(biāo)志會(huì)把每個(gè)下載的包都打印出來(lái);
    • ./... : 這個(gè)表示路徑,代表當(dāng)前目錄下所有的文件。

    docker go環(huán)境

    官網(wǎng)地址: https://hub.docker.com/_/golang/

    容器內(nèi)默認(rèn)工作區(qū)是 /go , 所以可以掛載到 /go/src 目錄下

    常見問(wèn)題

    • ssh 遠(yuǎn)程連進(jìn)去, 不知道為啥 沒有g(shù)o指令, 需要自己添加到環(huán)境變量中 .bash_profile

      export PATH=$PATH:/usr/local/go/bin GO_BIN=/usr/local/go/bin export GO_BIN

      然后使其生效 # source .bash_profile


    ubuntu 安裝 go

  • 下載 go1.10.3.linux-amd64.tar , 地址:https://golang.google.cn/dl/

  • 解壓: # tar zxvf go1.10.3.linux-amd64.tar.gz -C /usr/local

  • 增加環(huán)境變量

    # vi ~/.bash_profile ... export GOROOT=/usr/local/go export GOPATH=/mytemp/GoLab # 項(xiàng)目地址 export PATH=$PATH:$GOPATH:/usr/local/go/bin# source ~/.bash_profile # 使其生效
  • 查看命名, ok

    # go version go version go1.10.3 linux/amd64
    • 參考: https://blog.csdn.net/tao_627/article/details/79375950

    唯一ID生成

    • 分布式系統(tǒng)唯一ID生成方案匯總 - https://www.cnblogs.com/wyb628/p/7189772.html

    RabbitMQ

    • 我為什么要選擇RabbitMQ ,RabbitMQ簡(jiǎn)介,各種MQ選型對(duì)比 - https://www.sojson.com/blog/48.html

    反射 reflect

    • Go Reflect 性能 - https://colobu.com/2019/01/29/go-reflect-performance/

    優(yōu)化, 可以通過(guò) 自定義的生成器腳本, 生成, 避免使用反射


    Builder & Option 設(shè)計(jì)模式

    • 實(shí)例淺談利用Golang的Builder&Option設(shè)計(jì)模式來(lái)傳遞初始化參數(shù) - https://www.toutiao.com/a6768276240735404558/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1575900880&app=news_article&utm_source=mobile_qq&utm_medium=toutiao_ios&req_id=201912092214400100260790191A2D68A8&group_id=6768276240735404558

    GC

    • 深入理解Go-垃圾回收機(jī)制 - https://juejin.im/post/5d78b3276fb9a06b1829e691

    常見編譯報(bào)錯(cuò)

    • Q: can’t load package: package test: found packages main (base.go) and testgo (test_go.go) in E:\GoLab\src\test

      A: 同一個(gè)目錄下不能存在不同包名的文件

    declared and not used

    聲明但未被使用, 可以這樣屏蔽報(bào)錯(cuò)

    xf := xiaofang{} _ = xf

    踩坑

    保存自動(dòng)格式化, 源代碼被自動(dòng)刪除

    參考:

    https://github.com/microsoft/vscode-go/issues/2604

    https://stackoverflow.com/questions/48124565/why-does-vscode-delete-golang-source-on-save

    把格式化工具由 goreturns 換成 goformat, 同時(shí)取消掉保存自動(dòng)格式化

    "go.formatTool": "gofmt", // 使用 gofmt 工具格式化 "go.alternateTools": {"go-langserver": "gopls" },"[go]": {"editor.formatOnSave": false},

    go get 報(bào)錯(cuò): ‘xxx’ is not using a known version control system

    可能有兩個(gè)原因

  • 刪除本地的 xxx 目錄
  • 沒有連上 vpn
  • go run 報(bào)錯(cuò): The system cannot find the path specified.

    可能 go.mod 把某個(gè)包指向了本地 vendor 里面的包, 而 vendor 里面又沒有這個(gè)包.

    解決辦法參考: 使用本地包

    報(bào)錯(cuò): all goroutines are asleep - deadlock!

    main goroutine 在等一個(gè)永遠(yuǎn)不會(huì)來(lái)的數(shù)據(jù),那整個(gè)程序就永遠(yuǎn)等下去了, 這個(gè)時(shí)候就會(huì)報(bào)上述錯(cuò)誤

    需要用 sync.WaitGroup 來(lái)保證程序正常退出. 參考: test_error.go

    參考:

    https://stackoverflow.com/questions/26927479/go-language-fatal-error-all-goroutines-are-asleep-deadlock

    https://cloud.tencent.com/developer/article/1418106

    map 多線程訪問(wèn)加鎖

    即使是只是訪問(wèn), 不做 add 或 delete 操作, 都得加鎖, 不然會(huì)有潛在的錯(cuò)誤:

    runtime.mapaccess2_fast64(0x77e360, 0xc0000da000, 0xab1, 0x86f1e0, 0xc001325040)

    多線程訪問(wèn)的正確姿勢(shì) - 加鎖

    a.callMu.Lock() if ci, ok := a.callMap[pbData.Header.ReqID]; ok {ci.ch <- pbData } a.callMu.Unlock()

    channel 導(dǎo)致死鎖問(wèn)題

  • goroutine 3609 [chan receive]:
  • goroutine 3609 [chan send]:
  • goroutine 8 [running]:
  • 等問(wèn)題, 要不是 chan 沒被消費(fèi), 就是被過(guò)度消費(fèi). 例如:

    // https://juejin.im/post/5ca318e651882543db10d4ce // 測(cè)試死鎖 func Test_goroutinueDeadLock(t *testing.T) {ch := make(chan int)ch <- 5 }/* fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan receive]: testing.(*T).Run(0xc0000a8100, 0x5615e7, 0x17, 0x567ba0, 0x47c601)goroutine 6 [chan send]: GoLab/test_channel.Test_goroutinueDeadLock(0xc0000a8100)F:/a_link_workspace/go/GoWinEnv_new/src/GoLab/test_channel/channel_test.go:228 ch <- 5 */

    總結(jié)

    以上是生活随笔為你收集整理的Go_入坑笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 欧美成人综合视频 | 污污的视频在线免费观看 | av一片 | 久久综合久久网 | 国产视频二区三区 | 亚洲视频在线观看免费视频 | 思思久久久 | 一级黄色性片 | 超碰最新在线 | 美女131爽爽爽做爰视频 | 国产6区| 婷婷国产精品 | 一级片视频免费 | 美女福利视频在线观看 | 911美女片黄在线观看游戏 | 欧美综合图片 | 少妇又紧又深又湿又爽视频 | 日韩毛片av | 男生插女生视频在线观看 | 精品国产乱码久久久久久浪潮 | 激情av综合 | 国产视频在线免费观看 | 青青视频免费在线观看 | 欧美日韩国产在线一区 | 一区二区啪啪 | 国产精品色图 | 午夜性色福利影院 | 欧洲一区二区 | 国产一级全黄 | 亚洲电影影音先锋 | 五月天精品在线 | 日韩av中文字幕在线免费观看 | 人妻内射一区二区在线视频 | 黄色免费av网站 | 丁香激情六月 | 成人网入口 | 亚洲av日韩av在线观看 | 免费看欧美一级特黄a大片 国产免费的av | 免费禁漫天堂a3d | 国产色播 | 久久99精品久久久水蜜桃 | 精品九九在线 | 91视频在线网站 | 国产精彩视频在线观看 | 国语对白做受欧美 | 黑人干亚洲人 | 亚洲精品在线播放视频 | 91视频你懂的 | 黄色国产免费 | 欧美日韩三级在线观看 | 欧美成人xxxx | 正在播放国产一区 | 色七七久久 | 美女日日日 | 国产日韩欧美精品一区二区 | 一区二区欧美精品 | 亚洲图色av| 一区二区三区四区影院 | 国产农村妇女精品一区二区 | 青草视频在线播放 | 国产精品又黄又爽又色无遮挡 | 欧洲做受高潮欧美裸体艺术 | 欧美日韩高清一区二区 | 91超薄肉色丝袜交足高跟凉鞋 | 老头老太做爰xxx视频 | 久久av色| 人妻熟女一区二区三区app下载 | 中文字幕在线观看日韩 | wwwxxx在线观看 | 四虎黄网 | 亚洲在线播放 | 亚洲视频一二三区 | 久久久久麻豆v国产精华液好用吗 | 人妻一区二区三区四区五区 | 久久国内精品 | 国产成人a亚洲精品 | 午夜精品久久久久久久99黑人 | 你懂的网站在线 | 久久久一区二区三区 | 女人毛片视频 | 国产一级久久久久毛片精品 | 玖玖色在线 | 久久成人高清 | 最近日韩中文字幕 | 日韩在线第二页 | 双乳被四个男人吃奶h文 | 理论片一区 | 91精品国产综合久久久久久久 | 日韩一级网站 | 午夜在线一区二区三区 | 亚洲狠狠婷婷综合久久久久图片 | 伊人久久大香线蕉综合75 | 国产免费黄色网址 | 色婷婷综合成人 | 成人av在线一区二区 | 天天摸天天爽 | 久久亚洲精品小早川怜子 | 久久精品国产亚洲av麻豆色欲 | 艳妇臀荡乳欲伦交换gif |