主頁(yè) > 知識(shí)庫(kù) > 五步讓你成為GO 語(yǔ)言高手

五步讓你成為GO 語(yǔ)言高手

熱門(mén)標(biāo)簽:電銷機(jī)器人可以補(bǔ)救房產(chǎn)中介嗎 電梯外呼訪客系統(tǒng) 成都呼叫中心外呼系統(tǒng)平臺(tái) ?兓? 谷歌便利店地圖標(biāo)注 最短的地圖標(biāo)注 浙江人工智能外呼管理系統(tǒng) 百度地圖標(biāo)注搜索關(guān)鍵詞 騰訊外呼系統(tǒng)價(jià)格

Francesc (@francesc) 是 Go 核心團(tuán)隊(duì)的一員, 是提倡 Google Cloud 平臺(tái)的開(kāi)發(fā)者. 他是一個(gè)編程語(yǔ)言的愛(ài)好者, Google的技術(shù)指導(dǎo)大師, Go tour的創(chuàng)造者之一. 這個(gè)討論的靈感來(lái)自于另一個(gè) Raquel Vélez 在 JSConf. Slides 討論,這個(gè)討論已經(jīng)發(fā)到了這里.

Sourcegraph 是下一代編程協(xié)作工具, 用于搜索, 探索, 和審查代碼. 我們參加GopherCon India 來(lái)分享我們是怎樣使用 Go 并學(xué)習(xí)別人是怎樣使用它的, 對(duì)配合liveblog的這次討論我們深感榮幸.

作為Go團(tuán)隊(duì)的開(kāi)發(fā)者之一,F(xiàn)rancesc可能比世界上其他人接觸到的Go語(yǔ)言程序員都要多。正因?yàn)橛辛诉@樣的有利條件,他把Go語(yǔ)言的學(xué)習(xí)過(guò)程劃分為5個(gè)階段。

這些階段對(duì)于其他語(yǔ)言的學(xué)習(xí)也是成立的。理解自己處于哪個(gè)階段,可以幫助你找到提高自己的最有效的方法,也可以避免每個(gè)階段學(xué)習(xí)過(guò)程中的常見(jiàn)陷阱。

編者按:這篇文章對(duì)于每一個(gè)學(xué)習(xí)階段都給出了交互式的代碼片段。點(diǎn)擊函數(shù)名你就可以跳到具體的函數(shù)定義,方便進(jìn)行深入的研究。請(qǐng)看下文。

這里是GO程序員的五個(gè)進(jìn)化階段:

第一個(gè)階段(菜逼): 剛剛學(xué)習(xí)了這門(mén)語(yǔ)言。 已經(jīng)通過(guò)一些教程或者培訓(xùn)班了解基本的語(yǔ)法,可以寫(xiě)短的代碼片段。

第二個(gè)階段 (探索者): 可以寫(xiě)一個(gè)完整的程序,但不懂一些更高級(jí)的語(yǔ)言特征,比如“channels”。還沒(méi)有使用GO寫(xiě)一個(gè)大項(xiàng)目。

第三個(gè)階段(大手): 你能熟練的使用Go, 能夠用GO去解決,生產(chǎn)環(huán)境中一個(gè)具體和完整的問(wèn)題。已經(jīng)形成了一套自己的慣用法和常用代碼庫(kù)。在你的編碼方案中Go是一個(gè)非常好用的工具。

第四階段 (大神): 絕逼清楚Go語(yǔ)言的設(shè)計(jì)選擇和背后的動(dòng)機(jī)。能理解的簡(jiǎn)潔和可組合性哲學(xué)。

布道師: 積極地與他人分享關(guān)于Go語(yǔ)言知識(shí)和你對(duì)Go語(yǔ)言的理解。在各種合適的場(chǎng)所發(fā)出自己的聲音, 參與郵件列表、建立QQ群、做專題報(bào)告。成為一個(gè)布道者不見(jiàn)得是一個(gè)完全獨(dú)立的階段,這個(gè)角色可以在上述的任何一個(gè)階段中。

第一階段: 菜逼

菜鳥(niǎo)在這個(gè)階段使用Go去創(chuàng)建一些小項(xiàng)目或者玩具項(xiàng)目。他們應(yīng)該會(huì)利用到Go tour, Go playground, Go文檔, 和郵件列表(golang-nuts).

func main() {
    fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))}

這是Go語(yǔ)言寫(xiě)的簡(jiǎn)單例子,這個(gè)代碼段來(lái)自golang/example代碼庫(kù)里面的 hello.go 。 點(diǎn)擊就可以查看完整代碼擼。

一項(xiàng)重要的技能,新人應(yīng)該試著學(xué)習(xí)如何正確提問(wèn)。很多新人在郵件列表里面這樣說(shuō)“嘿,這報(bào)錯(cuò)了”,這并沒(méi)有提供足夠的信息,讓別人能理解并幫助他們解決問(wèn)題。別人看到的是一個(gè)粘貼了幾百行的代碼的帖子,并沒(méi)有花費(fèi)精力來(lái)重點(diǎn)說(shuō)明所遇到的問(wèn)題。

所以, 應(yīng)該盡量避免直接粘貼代碼到論壇。而應(yīng)該使用可以編輯并且可以在瀏覽器中直接運(yùn)行的Go playground的“分享”按鈕鏈接到代碼片段。

Phase 2: the explorer

探索者已經(jīng)可以使用Go寫(xiě)一些小的軟件,但有時(shí)仍然會(huì)有些迷茫。他們可能不完全明白怎么使用Go的高級(jí)特性,比如通道。雖然他們還有很多東西要學(xué)習(xí),但已掌握的足夠做一些有用的事情了!他們開(kāi)始對(duì)Go的潛能有感覺(jué)了,并對(duì)它們能使用Go創(chuàng)建的東西感到興奮。

在探索階段通常會(huì)經(jīng)歷兩個(gè)步驟。第一,膨脹的預(yù)期達(dá)到頂點(diǎn),你覺(jué)得可以用Go做所有的事情,但還并不能明白或領(lǐng)悟到Go的真諦。你大概會(huì)用所熟悉的語(yǔ)言的模式和慣用語(yǔ)來(lái)寫(xiě)Go代碼,但對(duì)于什么是地道的Go,還沒(méi)有比較強(qiáng)烈的感覺(jué)。你開(kāi)始嘗試著手干這樣的事情--“遷移架構(gòu)X,從Y語(yǔ)言到Go語(yǔ)言”。

到達(dá)預(yù)期膨脹的頂點(diǎn)之后,你會(huì)遇到理想幻滅的低谷。你開(kāi)始想念語(yǔ)言Y的特性X,此時(shí)你還沒(méi)有完全的掌握地道的Go。你還在用其他編程語(yǔ)言的風(fēng)格來(lái)寫(xiě)Go語(yǔ)言的程序,你甚至開(kāi)始覺(jué)得沮喪。你可能在大量使用reflect和unsafe這兩個(gè)包,但這不是地道的Go。地道的Go不會(huì)使用那些魔法一樣的東西。

這個(gè)探索階段產(chǎn)生的項(xiàng)目的一個(gè)很好的例子就是Martini Web框架。Martini是一個(gè)Go語(yǔ)言的早期Web框架,它從Ruby的Web框架當(dāng)中吸收了很多思想(比如依賴注入)。最初,這個(gè)框架在社區(qū)中引起了強(qiáng)烈的反響,但是它逐漸在性能和可調(diào)試性上受到了一些批評(píng)。Martini框架的作者Jeremy Saenz積極響應(yīng)這些來(lái)自Go社區(qū)的反饋,寫(xiě)了一個(gè)更加符合Go語(yǔ)言規(guī)范的庫(kù)Negroni

func (m *Martini) RunOnAddr(addr string) {
    // TODO: Should probably be implemented using a new instance of http.Server in place of
    // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use.
    // This would also allow to improve testing when a custom host and port are passed.
 
    logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
    logger.Printf("listening on %s (%s)\n", addr, Env)
    logger.Fatalln(http.ListenAndServe(addr, m))}

來(lái)自Martini框架的交互式代碼片段,它是不地道的Go的例子。注意用反射包實(shí)現(xiàn)的依賴注入

func TestNegroniServeHTTP(t *testing.T) {
    result := ""
    response := httptest.NewRecorder()
 
    n := New()
    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        result += "foo"
        next(rw, r)
        result += "ban"
    }))
    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        result += "bar"
        next(rw, r)
        result += "baz"
    }))
    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        result += "bat"
        rw.WriteHeader(http.StatusBadRequest)
    }))
 
    n.ServeHTTP(response, (*http.Request)(nil))
 
    expect(t, result, "foobarbatbazban")
    expect(t, response.Code, http.StatusBadRequest)}

來(lái)自Negroni庫(kù)的交互式代碼片段,它是地道的Go的例子

其他語(yǔ)言在提供一些核心功能,比如HTTP處理的時(shí)候,往往需要依賴第三方庫(kù)。但是Go語(yǔ)言在這一點(diǎn)上很不同,它的標(biāo)準(zhǔn)庫(kù)非常強(qiáng)大。如果你認(rèn)為Go標(biāo)準(zhǔn)庫(kù)沒(méi)有強(qiáng)大到可以做你想做的事情,那么我說(shuō)你錯(cuò)了。Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)難以置信的強(qiáng)大,值得你花時(shí)間閱讀它的代碼,學(xué)習(xí)它實(shí)現(xiàn)的模式。

func (srv *Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}

Go標(biāo)準(zhǔn)庫(kù)中的ListenAndServe函數(shù)片段。如果你寫(xiě)過(guò)Go程序,你可能已經(jīng)調(diào)用過(guò)這個(gè)函數(shù)很多次了,但是你曾經(jīng)花時(shí)間看過(guò)它的實(shí)現(xiàn)么?去點(diǎn)擊上面的代碼片段吧。

幻滅的低谷中的幻滅感來(lái)自于這樣的事實(shí):你還在用其他語(yǔ)言的模式來(lái)想問(wèn)題,而且你還沒(méi)有完全探索過(guò)Go能提供給你什么。下面是一些好玩的事情,你可以做一下來(lái)打破困境,進(jìn)一步探索這門(mén)語(yǔ)言中好玩的事。

go generate

現(xiàn)在來(lái)看看go generate。go generate是一個(gè)你可以用來(lái)自動(dòng)自成Go代碼的命令。你可以結(jié)合例如jsonenums(一個(gè)用于為枚舉類型自動(dòng)生成JSON編組樣板代碼的類庫(kù))這樣的元編程來(lái)使用go generate快速自動(dòng)實(shí)現(xiàn)重復(fù)乏味代碼的編寫(xiě)。在Go標(biāo)準(zhǔn)類庫(kù)里面已經(jīng)有大量可以用于解析AST的接口,而AST使得編寫(xiě)元編程工具更簡(jiǎn)單,更容易。在會(huì)議上,有另外兩次討論(Go語(yǔ)言中的元編程實(shí)踐和擁抱標(biāo)準(zhǔn)類庫(kù))談及到了這一點(diǎn)。

func main() {
    flag.Parse()
    if len(*typeNames) == 0 {
        log.Fatalf("the flag -type must be set")
    }
    types := strings.Split(*typeNames, ",")
 
    // Only one directory at a time can be processed, and the default is ".".
    dir := "."
    if args := flag.Args(); len(args) == 1 {
        dir = args[0]
    } else if len(args) > 1 {
        log.Fatalf("only one directory at a time")
    }
 
    pkg, err := parser.ParsePackage(dir, *outputSuffix+".go")
    if err != nil {
        log.Fatalf("parsing package: %v", err)
    }
 
    var analysis = struct {
        Command        string
        PackageName    string
        TypesAndValues map[string][]string
    }{
        Command:        strings.Join(os.Args[1:], " "),
        PackageName:    pkg.Name,
        TypesAndValues: make(map[string][]string),
    }
 
    // Run generate for each type.
    for _, typeName := range types {
        values, err := pkg.ValuesOfType(typeName)
        if err != nil {
            log.Fatalf("finding values for type %v: %v", typeName, err)
        }
        analysis.TypesAndValues[typeName] = values
 
        var buf bytes.Buffer
        if err := generatedTmpl.Execute(buf, analysis); err != nil {
            log.Fatalf("generating code: %v", err)
        }
 
        src, err := format.Source(buf.Bytes())
        if err != nil {
            // Should never happen, but can arise when developing this code.
            // The user can compile the output to see the error.
            log.Printf("warning: internal error: invalid Go generated: %s", err)
            log.Printf("warning: compile the package to analyze the error")
            src = buf.Bytes()
        }
 
        output := strings.ToLower(typeName + *outputSuffix + ".go")
        outputPath := filepath.Join(dir, output)
        if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
            log.Fatalf("writing output: %s", err)
        }
    }}

一段互動(dòng)的片段演示了如何編寫(xiě)jsonenums命令。

OpenGL

許多人使用Go作web服務(wù),但是你知道你也可以用Go寫(xiě)出很cool的圖形應(yīng)用嗎?查看Go在OpenGL中的捆綁。

func main() {
    glfw.SetErrorCallback(errorCallback)
 
    if !glfw.Init() {
        panic("Can't init glfw!")
    }
    defer glfw.Terminate()
 
    window, err := glfw.CreateWindow(Width, Height, Title, nil, nil)
    if err != nil {
        panic(err)
    }
 
    window.MakeContextCurrent()
 
    glfw.SwapInterval(1)
 
    gl.Init()
 
    if err := initScene(); err != nil {
        fmt.Fprintf(os.Stderr, "init: %s\n", err)
        return
    }
    defer destroyScene()
 
    for !window.ShouldClose() {
        drawScene()
        window.SwapBuffers()
        glfw.PollEvents()
    }}

交互式的片段正說(shuō)明Go的OpenGL捆綁能制作Gopher cube。點(diǎn)擊函數(shù)或方法名去探索。

黑客馬拉松和挑戰(zhàn)

你也可以觀看挑戰(zhàn)和黑客馬拉松,類似Gopher Gala和Go Challenge。在過(guò)去,來(lái)自世界各地的程序員一起挑戰(zhàn)一些真實(shí)的酷項(xiàng)目,你可以從中獲取靈感。

第三階段: 老手

作為一個(gè)老手,這意味著你可以解決很多Go語(yǔ)言中你關(guān)心的問(wèn)題。新的需要解決的問(wèn)題會(huì)帶來(lái)新的疑問(wèn),經(jīng)過(guò)試錯(cuò),你學(xué)會(huì)了在這門(mén)語(yǔ)言中什么是可以做的,什么是不能做的。此時(shí),你已經(jīng)對(duì)這門(mén)語(yǔ)言的習(xí)慣和模式有了一個(gè)堅(jiān)實(shí)的理解。你可以非常高效地工作,寫(xiě)出可讀,文檔完善,可維護(hù)的代碼。

成為老手的一個(gè)很好的方法就是在大項(xiàng)目上工作。如果你自己有一個(gè)項(xiàng)目的想法,開(kāi)始動(dòng)手去做吧(當(dāng)然你要確定它并不是已經(jīng)存在了)。大多數(shù)人也許并沒(méi)有一個(gè)很大的項(xiàng)目的想法,所以他們可以對(duì)已經(jīng)存在的項(xiàng)目做出貢獻(xiàn)。Go語(yǔ)言已經(jīng)有很多大型項(xiàng)目,而且它們正在被廣泛使用,比如Docker, Kubernetes和Go本身。可以看看這個(gè)項(xiàng)目列表

func (cli *DockerCli) CmdRestart(args ...string) error {
    cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)
    nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container.")
    cmd.Require(flag.Min, 1)
 
    utils.ParseFlags(cmd, args, true)
 
    v := url.Values{}
    v.Set("t", strconv.Itoa(*nSeconds))
 
    var encounteredError error
    for _, name := range cmd.Args() {
        _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
        if err != nil {
            fmt.Fprintf(cli.err, "%s\n", err)
            encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
        } else {
            fmt.Fprintf(cli.out, "%s\n", name)
        }
    }
    return encounteredError}

Docker項(xiàng)目的交互式代碼片段。點(diǎn)擊函數(shù)名,開(kāi)始探索之旅吧。

老手應(yīng)該對(duì)Go生態(tài)系統(tǒng)的工具有一個(gè)很強(qiáng)的掌握,因?yàn)檫@些工具真的提高生產(chǎn)效率。你應(yīng)該了解go generate,go vet,go test-race, 和gofmt/goimports/goreturns。你應(yīng)該使用go fmt,因?yàn)樗鼤?huì)自動(dòng)把你的代碼按照Go社區(qū)的風(fēng)格標(biāo)準(zhǔn)來(lái)格式化。goimports可以做同樣的事情,而且還會(huì)添加丟失的imports。goretures不光做了前面所說(shuō)的事情,還可以在返回表達(dá)式添加丟失的錯(cuò)誤,這是大家都討厭的地方。

在老手階段,你一定要開(kāi)始做code review。code review的意義并不是要修改或者找到錯(cuò)誤(那是測(cè)試人員做的事情)。code review可以幫助維持統(tǒng)一的編程風(fēng)格,提高軟件的總體質(zhì)量,還可以在別人的反饋中提高你自己的編程技術(shù)。幾乎所有的大型開(kāi)源項(xiàng)目都對(duì)每一個(gè)提交做code review。

下面是一個(gè)從人類的反饋當(dāng)中學(xué)習(xí)的例子:Google的Go團(tuán)隊(duì)以前都在main函數(shù)的外面聲明命令行標(biāo)記。在去年的GopherCon會(huì)議上,F(xiàn)rancesc遇到了SoundCloud公司的Peter Bourgon(@peterbourgon)。Peter Bourgon說(shuō)在SoundCloud,他們都在main函數(shù)內(nèi)部聲明標(biāo)記,這樣他們不會(huì)錯(cuò)誤地在外部使用標(biāo)記。Francesc現(xiàn)在認(rèn)為這是最佳實(shí)踐。

第四階段:專家

作為一個(gè)專家,你很好地了解了語(yǔ)言的哲學(xué)思想。對(duì)于Go語(yǔ)言的特性,你知道何時(shí)應(yīng)該使用,何時(shí)不應(yīng)該使用。例如,Jeremy Saenz在dotGo風(fēng)暴討論中談?wù)摰搅撕螘r(shí)不該使用接口。

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
    call := new(Call)
    call.ServiceMethod = serviceMethod
    call.Args = args
    call.Reply = reply
    if done == nil {
        done = make(chan *Call, 10) // buffered.
    } else {
        // If caller passes done != nil, it must arrange that
        // done has enough buffer for the number of simultaneous
        // RPCs that will be using that channel.  If the channel
        // is totally unbuffered, it's best not to run at all.
        if cap(done) == 0 {
            log.Panic("rpc: done channel is unbuffered")
        }
    }
    call.Done = done
    client.send(call)
    return call}

來(lái)自標(biāo)準(zhǔn)類庫(kù)的一小塊交互代碼片段使用了頻道。理解標(biāo)準(zhǔn)類庫(kù)里面的模式背后的決策原因是成為一個(gè)專家必經(jīng)之路。

但是不要成為只局限于單一語(yǔ)言的專家。跟其他任何語(yǔ)言一樣,Go僅僅只是一個(gè)工具。你還應(yīng)該去探索其他語(yǔ)言,并且學(xué)習(xí)他們的模式和風(fēng)格。Francesc從他使用Go的經(jīng)驗(yàn)中找到了編寫(xiě)JavaScript的啟發(fā)。他還喜歡重點(diǎn)關(guān)注于不可變性和致力于避免易變性的Haskell語(yǔ)言,并從中獲得了如何編寫(xiě)Go代碼的靈感。

布道者

作為一個(gè)布道者,你分享自己的知識(shí),傳授你學(xué)會(huì)的和你提出的最佳實(shí)踐。你可以分享自己對(duì)Go喜歡或者不喜歡的地方。全世界各地都有Go會(huì)議,找到離你最近的。

你可以在任何一個(gè)階段成為布道者,不要等到你成為這個(gè)領(lǐng)域的專家的時(shí)候才發(fā)出自己的聲音。在你學(xué)習(xí)Go的任何一個(gè)階段,提出問(wèn)題,結(jié)合你的經(jīng)驗(yàn)給出反饋,不要羞于提出自己不喜歡的地方。你提出的反饋可以幫助社區(qū)改善做事情的方法,也可能改變你自己對(duì)編程的看法。

func main() {
    httpAddr := flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
    originHost := flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
    flag.StringVar(basePath, "base", "", "base path for slide template and static resources")
    flag.BoolVar(present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
    nativeClient := flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
    flag.Parse()
 
    if basePath == "" {
        p, err := build.Default.Import(basePkg, "", build.FindOnly)
        if err != nil {
            fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)
            fmt.Fprintf(os.Stderr, basePathMessage, basePkg)
            os.Exit(1)
        }
        basePath = p.Dir
    }
    err := initTemplates(basePath)
    if err != nil {
        log.Fatalf("Failed to parse templates: %v", err)
    }
 
    ln, err := net.Listen("tcp", *httpAddr)
    if err != nil {
        log.Fatal(err)
    }
    defer ln.Close()
 
    _, port, err := net.SplitHostPort(ln.Addr().String())
    if err != nil {
        log.Fatal(err)
    }
    origin := url.URL{Scheme: "http"}
    if *originHost != "" {
        origin.Host = net.JoinHostPort(*originHost, port)
    } else if ln.Addr().(*net.TCPAddr).IP.IsUnspecified() {
        name, _ := os.Hostname()
        origin.Host = net.JoinHostPort(name, port)
    } else {
        reqHost, reqPort, err := net.SplitHostPort(*httpAddr)
        if err != nil {
            log.Fatal(err)
        }
        if reqPort == "0" {
            origin.Host = net.JoinHostPort(reqHost, port)
        } else {
            origin.Host = *httpAddr
        }
    }
 
    if present.PlayEnabled {
        if *nativeClient {
            socket.RunScripts = false
            socket.Environ = func() []string {
                if runtime.GOARCH == "amd64" {
                    return environ("GOOS=nacl", "GOARCH=amd64p32")
                }
                return environ("GOOS=nacl")
            }
        }
        playScript(basePath, "SocketTransport")
        http.Handle("/socket", socket.NewHandler(origin))
    }
    http.Handle("/static/", http.FileServer(http.Dir(basePath)))
 
    if !ln.Addr().(*net.TCPAddr).IP.IsLoopback()
        present.PlayEnabled !*nativeClient {
        log.Print(localhostWarning)
    }
 
    log.Printf("Open your web browser and visit %s", origin.String())
    log.Fatal(http.Serve(ln, nil))

流行的present命令的main函數(shù),很多Go的用戶使用它來(lái)制作幻燈片。許多演講者修改了這個(gè)模塊來(lái)滿足自己的需要。

QA

問(wèn):在GO語(yǔ)言中,我所懷念的一項(xiàng)功能是一個(gè)好的調(diào)試器。

答:我們正在做了,不只是調(diào)試器,我們還會(huì)提供一個(gè)更好的總體監(jiān)視工具可以讓你在程序運(yùn)行時(shí)更好地洞察程序在干什么(顯示出所有正在運(yùn)行的goroutine的狀態(tài))。在GO 1.5中探索它吧。

以上所述就是本文的全部?jī)?nèi)容了,希望大家能夠喜歡。

您可能感興趣的文章:
  • Go語(yǔ)言的GOPATH與工作目錄詳解
  • Go語(yǔ)言中的Array、Slice、Map和Set使用詳解
  • Go語(yǔ)言interface詳解
  • Go語(yǔ)言命令行操作命令詳細(xì)介紹
  • Go語(yǔ)言運(yùn)行環(huán)境安裝詳細(xì)教程
  • GO語(yǔ)言數(shù)組和切片實(shí)例詳解
  • Go語(yǔ)言創(chuàng)建、初始化數(shù)組的常見(jiàn)方式匯總
  • Go語(yǔ)言常用字符串處理方法實(shí)例匯總
  • GO語(yǔ)言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法實(shí)例
  • Go程序性能優(yōu)化及pprof使用方法詳解

標(biāo)簽:邢臺(tái) 盤(pán)錦 眉山 上海 雅安 宜昌 紹興 七臺(tái)河

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《五步讓你成為GO 語(yǔ)言高手》,本文關(guān)鍵詞  五步,讓你,成為,語(yǔ)言,高手,;如發(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)文章
  • 下面列出與本文章《五步讓你成為GO 語(yǔ)言高手》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于五步讓你成為GO 語(yǔ)言高手的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章