Go语言入门分享
簡介:?Go語言出自Ken Thompson、Rob Pike和Robert Griesemer之手,起源于2007年,并在2009年正式對外發(fā)布。Go的主要目標是“兼具Python等動態(tài)語言的開發(fā)速度和C/C++等編譯型語言的性能與安全性”,旨在不損失應用程序性能的情況下降低代碼的復雜性,具有“部署簡單、并發(fā)性好、語言設計良好、執(zhí)行性能好”等優(yōu)勢。
作者 | 賦行
來源 | 阿里技術公眾號
前言
曾經我是一名以Java語言為主的開發(fā)者,做過JavaWeb相關的開發(fā),后來轉Android,還是離不開Java,直到轉去做大前端了,其實也就是一直在用JS寫業(yè)務。如今由于個人發(fā)展原因,來到阿里云,由于項目需要就擼起了Go語言;多年編程經驗告訴我,語言只是工具罷了,重要的還是其思想與邏輯,所以只需學學語法就好了,于是我便三天入門Go,期間主要用Java和JS來類比,語法變化之大,差點讓我從入門到放棄了!其實,還真不是學習語法就好了呢,其中包含了很多Go的設計理念。正所謂好記性不如敲爛鍵盤,學過的東西,還是要沉淀沉淀,也可以分享出來一起探討,更有助于成長,于是我就簡單記錄了一下我的Go語言入門學習筆記。
一 簡介
Go語言出自Ken Thompson、Rob Pike和Robert Griesemer之手,起源于2007年,并在2009年正式對外發(fā)布,其實都是Google的,設計Go語言的初衷都是為了滿足Google的需求。Go的主要目標是“兼具Python等動態(tài)語言的開發(fā)速度和C/C++等編譯型語言的性能與安全性”,旨在不損失應用程序性能的情況下降低代碼的復雜性,具有“部署簡單、并發(fā)性好、語言設計良好、執(zhí)行性能好”等優(yōu)勢。最主要還是為了并發(fā)而生,并發(fā)是基于goroutine的,goroutine類似于線程,但并非線程,可以將goroutine理解為一種虛擬線程。Go語言運行時會參與調度goroutine,并將goroutine合理地分配到每個CPU中,最大限度地使用CPU性能。
二 環(huán)境
我們玩Java的時候需要下載JDK,類似于此,用Go開發(fā)也需要下載Go,里面提供各種develop-kit、library以及編譯器。在官網下載mac版本pkg后直接安裝,最后用 go version 命令驗證版本:
然后就是設置這兩個環(huán)境變量,mac系統(tǒng)是在 .bash_profile 文件里面:
export GOROOT=/usr/local/go export GOPATH=$HOME/go- GOROOT:表示的是Go語言編譯、工具、標準庫等的安裝路徑,其實就相當于配置JAVA_HOME那樣。
- GOPATH:這個和Java有點不一樣,Java里并不需要設置這個變量,這個表示Go的工作目錄,是全局的,當執(zhí)行Go命令的時候會依賴這個目錄,相當于一個全局的workspace。一般還會把$GOPATH/bin設置到PATH目錄,這樣編譯過的代碼就可以直接執(zhí)行了。
1 純文本開發(fā)
編寫代碼,可以保存在任意地方,例如新建一個helloworld目錄,創(chuàng)建hello.go文件:
package main import "fmt" func main() { fmt.Println("hello, world") }然后執(zhí)行 go build hello.go 就可以編譯出hello文件,在./hello就可以執(zhí)行了;或者直接 go run hello.go 合二為一去執(zhí)行。執(zhí)行這個命令并不需要設置環(huán)境變量就可以了。看起來和c差不多,但是和Java不一樣,運行的時候不需要虛擬機。早期的GO工程也是使用Makefile來編譯,后來有了強大的命令 go build、go run,可以直接識別目錄還是文件。
2 GoLand
自動import,超爽的體驗!不用按command + /了!
運行項目需要設置build config,和Android、Java的都差不多,例如創(chuàng)建一個hello-goland項目:
導入go module項目的時候需要勾選這項,否則無法像maven/gradle那樣sync下載依賴:
3 VSCODE
直接搜索Go插件,第一個最多安裝量的就是了,我還沒用過所以不太清楚如何。
三 工程結構
在設置GOPATH環(huán)境變量的時候,這個目錄里面又分了三個子目錄bin、pkg、src,分別用于存放可執(zhí)行文件、包文件和源碼文件。當我們執(zhí)行Go命令的時候,如果我們指定的不是當前目錄的文件或者絕對路徑的目錄的話,就會去GOPATH目錄的去找。這樣在GOPATH目錄創(chuàng)建了xxx的目錄后,就可以在任意地方執(zhí)行 go build xx 命令來構建或者運行了。
pkg目錄應該是在執(zhí)行 go install 后生成的包文件,包括.a這樣的文件,相當于一個歸檔。
├── bin │ ├── air │ ├── govendor │ ├── swag │ └── wire ├── pkg │ ├── darwin_amd64 │ ├── mod │ └── sumdb └── src├── calc├── gin-blog├── github.com├── golang.org├── google.golang.org├── gopkg.in└── simplemath這樣對于我們具體項目來說并不好,沒有Workspace的概念來隔離每個項目了,所以我覺得這個GOPATH目錄放的應該是公用的項目,例如開源依賴的。我們在開發(fā)過程中,也會下載很多的依賴,這些依賴都下載到這個目錄,和我們的項目文件混在一起了。
另外,通過IDE可以設置project的GOPATH,相當于在執(zhí)行的時候給GOPATH增加了一個目錄變量,也就是說,我們創(chuàng)建一個項目,然后里面也有bin、src、pkg這三個目錄,和GOPATH一樣的,本質上,IDE在運行的時候其實就是設置了一下GOPATH:
GOPATH=/Users/fuxing/develop/testgo/calc-outside:/Users/fuxing/develop/go #gosetupGo語言在尋找變量、函數(shù)、類屬性及方法的時候,會先查看GOPATH這個系統(tǒng)環(huán)境變量,然后根據(jù)該變量配置的路徑列表依次去對應路徑下的src目錄下根據(jù)包名查找對應的目錄,如果對應目錄存在,則再到該目錄下查找對應的變量、函數(shù)、類屬性和方法。
其實官方提供了Go Modules的方法更好解決。
1 Go Modules
從Go 1.11版本開始,官方提供了Go Modules管理項目和依賴,從1.13版本開始,更是默認開啟了對Go Modules的支持,使用Go Modules的好處是顯而易見的 —— 不需要再依賴GOPATH,你可以在任何位置創(chuàng)建Go項目,并且在國內,可以通過 GOPROXY 配置鏡像源加速依賴包的下載。也就是說,創(chuàng)建一個項目就是一個mod,基本上目前Go開源項目都是這樣做的。其實就是類似于Maven和Gradle。
// 創(chuàng)建mod項目,也是可以用IDE來new一個mod項目的: go mod init calc-mod// 一般開源在github上面的項目名字是這樣的;和maven、gradle不一樣的是,開發(fā)完成根本不需要發(fā)布到倉庫!只要提交代碼后打tag就可以了 go mod init github.com/fuxing-repo/fuxing-module-name// 創(chuàng)建一個模塊:執(zhí)行這個命令主要是多了一個go.mod文件,里面就一行內容: module calc-mod// import以后,執(zhí)行下載依賴命令,不需要編輯go.mod文件。依賴會下載到GOPATH/pkg/mod目錄 go list用GoLand來打開不同的項目,顯示依賴的外部庫是不一樣的,如果是用GOPATH創(chuàng)建的項目,需要用命令下載依賴包到GOPATH:
go get -u github.com/fuxing-repo/fuxing-module-name四 語法
1 包:Package 和 Import
Java里面的包名一般是很長的,和文件夾名稱對應,作用就是命名空間,引入的時候需要寫長長的一串,也可以用通配符:
Go里面一般的包名是當前的文件夾名稱,同一個項目里面,可以存在同樣的包名,如果同時都需要引用同樣包名的時候,就可以用alias區(qū)分,類似于JS那樣。一般import的是一個包,不像Java那樣import具體的類。同一個包內,不同文件,但是里面的東西是可以使用的,不需要import。這有點類似于C的include吧。如果多行的話,用括號換行包起來。
Go語言中,無論是變量、函數(shù)還是類屬性及方法,它們的可見性都是與包相關聯(lián)的,而不是類似Java那樣,類屬性和方法的可見性封裝在對應的類中,然后通過 private、protected 和 public 這些關鍵字來描述其可見性,Go語言沒有這些關鍵字,和變量和函數(shù)一樣,對應Go語言的自定義類來說,屬性和方法的可見性根據(jù)其首字母大小寫來決定,如果屬性名或方法名首字母大寫,則可以在其他包中直接訪問這些屬性和方法,否則只能在包內訪問,所以Go語言中的可見性都是包一級的,而不是類一級的。
在Java里面,只有靜態(tài),或者對象就可以使用點運算符,而且是極其常用的操作,而在Go里面,還可以用一個包名來點,這就是結合了import來使用,可以點出一個函數(shù)調用,也可以點出一個結構體,一個接口。另外區(qū)別于C,不管是指針地址,還是對象引用,都是用點運算符,不需要考慮用點還是箭頭了!
入口的package必須是main,否則可以編譯成功,但是跑不起來:
Compiled binary cannot be executed.原因就是找不到入口函數(shù),跟C和Java一樣吧,也需要main函數(shù)。
2 變量
- 用 var 關鍵字修飾(類似于JS),有多個變量的時候用括號 () 包起來,默認是有初始化值的,和Java一樣。
- 如果初始化的時候就賦值了那可以不需要 var 來修飾,和Java不同的是變量類型在變量后面而不是前面,不過需要 := 符號。
- 最大的變化就是類型在變量后面!
- 語句可以省略分號 ;
多重賦值
i, j = j, i可以實現(xiàn)變量交換,有點像JS的對象析構,但是其實不一樣。有了這個能力,函數(shù)是可以返回多個值了!
匿名變量
用 _ 來表示,作用就是可以避免創(chuàng)建定義一些無意義的變量,還有就是不會分配內存。
指針變量
和C語言一樣的,回想一下交換值的例子即可,到底傳值和傳址作為參數(shù)的區(qū)別是啥。
Go語言之所以引入指針類型,主要基于兩點考慮,一個是為程序員提供操作變量對應內存數(shù)據(jù)結構的能力;另一個是為了提高程序的性能(指針可以直接指向某個變量值的內存地址,可以極大節(jié)省內存空間,操作效率也更高),這在系統(tǒng)編程、操作系統(tǒng)或者網絡應用中是不容忽視的因素。
指針在Go語言中有兩個使用場景:類型指針和數(shù)組切片。
作為類型指針時,允許對這個指針類型的數(shù)據(jù)進行修改指向其它內存地址,傳遞數(shù)據(jù)時如果使用指針則無須拷貝數(shù)據(jù)從而節(jié)省內存空間,此外和C語言中的指針不同,Go語言中的類型指針不能進行偏移和運算,因此更為安全。
變量類型
Go語言內置對以下這些基本數(shù)據(jù)類型的支持:
- 布爾類型:bool
- 整型:int8、byte、int16、int、uint、uintptr 等
- 浮點類型:float32、float64
- 復數(shù)類型:complex64、complex128
- 字符串:string
- 字符類型:rune,本質上是uint32
- 錯誤類型:error
此外,Go語言也支持以下這些復合類型:
- 指針(pointer)
- 數(shù)組(array)
- 切片(slice)
- 字典(map)
- 通道(chan)
- 結構體(struct)
- 接口(interface)
還有const常量,iota這個預定義常量用來定義枚舉??梢员徽J為是一個可被編譯器修改的常量,在每一個const關鍵字出現(xiàn)時被重置為0,然后在下一個const出現(xiàn)之前,每出現(xiàn)一次iota,其所代表的數(shù)字會自動增1。
const (Sunday = iota Monday Tuesday Wednesday Thursday Friday Saturday numberOfDays )類型強轉
v1 := 99.99 v2 := int(v1) // v2 = 99v1 := []byte{'h', 'e', 'l', 'l', 'o'} v2 := string(v1) // v2 = hello//字符相關的轉化一般用strconv包 v1 := "100" v2, err := strconv.Atoi(v1) // 將字符串轉化為整型,v2 = 100v3 := 100 v4 := strconv.Itoa(v3) // 將整型轉化為字符串, v4 = "100"//結構體類型轉換 //類型斷言 //x.(T) 其實就是判斷 T 是否實現(xiàn)了 x 接口,如果實現(xiàn)了,就把 x 接口類型具體化為 T 類型; claims, ok := tokenClaims.Claims.(*jwt.StandardClaims)數(shù)組與切片
//定義數(shù)組 var a [8]byte // 長度為8的數(shù)組,每個元素為一個字節(jié) var b [3][3]int // 二維數(shù)組(9宮格) var c [3][3][3]float64 // 三維數(shù)組(立體的9宮格) var d = [3]int{1, 2, 3} // 聲明時初始化 var e = new([3]string) // 通過 new 初始化 var f = make([]string, 3) // 通過 make初始化//初始化 a := [5]int{1,2,3,4,5} b := [...]int{1, 2, 3}//切片 b := []int{} //數(shù)組切片slice就是一個可變長數(shù)組 c := a[1:3] // 有點類似于subString,或者js.slice d := make([]int, 5) //make相當于,new、alloc,用來分配內存//數(shù)組的長度 length := len(a)//添加一個元素 b = append(b, 4)字典
其實就是Java里的map,使用上語法有很多不同。
var testMap map[string]int testMap = map[string]int{"one": 1,"two": 2,"three": 3, } //還可以這樣初始化: var testMap = make(map[string]int) //map[string]int{} testMap["one"] = 1 testMap["two"] = 2 testMap["three"] = 3make和new
// The make built-in function allocates and initializes an object of type // slice, map, or chan (only). Like new, the first argument is a type, not a // value. Unlike new, make's return type is the same as the type of its // argument, not a pointer to it. The specification of the result depends on // the type: // Slice: The size specifies the length. The capacity of the slice is // equal to its length. A second integer argument may be provided to // specify a different capacity; it must be no smaller than the // length. For example, make([]int, 0, 10) allocates an underlying array // of size 10 and returns a slice of length 0 and capacity 10 that is // backed by this underlying array. // Map: An empty map is allocated with enough space to hold the // specified number of elements. The size may be omitted, in which case // a small starting size is allocated. // Channel: The channel's buffer is initialized with the specified // buffer capacity. If zero, or the size is omitted, the channel is // unbuffered. func make(t Type, size ...IntegerType) Type// The new built-in function allocates memory. The first argument is a type, // not a value, and the value returned is a pointer to a newly // allocated zero value of that type. func new(Type) *Type區(qū)別就是返回值和參數(shù)不同,一個是值,一個是指針,slice、chan、map只能用make,本身就是指針。其他make、new都行。
神奇的nil
Java里面用null比較舒服,直接就判空了,除了在string類型的時候,還要判斷字符為 "",但是Go里面的string要判斷為空就簡單一點,不能判斷nil,只能判斷 ""。然而Go里面的nil卻和null不一樣,其實是和JS里面 ==、=== 很像。
nil也是有類型的。
func Foo() error {var err *os.PathError = nil// …return err //實際返回的是[nil, *os.PathError]//return nil //正確的方式是直接return nil 實際返回的是[nil, nil] }func main() {err := Foo()fmt.Println(err) // <nil>fmt.Println(err == nil) // falsefmt.Println(err == (*os.PathError)(nil)) //true }根對象:Object
在Java里面,如果不用多態(tài),沒有接口,父類,超類的話,就用Object作為根對象,在Go里面,如果函數(shù)參數(shù)不知道用什么類型,通常會用 interface{},這是個空接口,表示任意類型,因為不是弱類型語言,沒有any類型,也不是強面向對象語言,沒有Object,所以就有這個空接口的出現(xiàn)。
3 語句
比較大的一個特點就是能不用括號的地方都不用了。
控制流程
if語句的判斷條件都沒有了括號包起來,還可以前置寫變量初始化語句,類似于for循環(huán),左花括號 { 必須與 if 或者 else 處于同一行。
switch語句變得更強大了,有這些變化:
- switch關鍵字后面可以不跟變量,這樣case后面就必須跟條件表達式,其實本質上就是美化了if-else-if。
- 如果switch后面跟變量,case也變得強大了,可以出現(xiàn)多個結果選項,通過逗號分隔。
- swtich后面還可以跟一個函數(shù)。
- 不需要用break來明確退出一個case,如果要穿透執(zhí)行一層,可以用 fallthrough 關鍵字。
循環(huán)流程
去掉了 while、repeat 這些關鍵字了,只保留了 for 這個關鍵字,其實用起來差不多。break , continue 這些關鍵字還是有的。
//通用的用法 for i := 1; i <= 5; i++ {fmt.Println(i) }//類似于while的用法 a := 1 for a <= 5 {fmt.Println(a)a ++ }//死循環(huán) for {// do something } for ;; {// do something }//類似java for-each的用法 listArray := [...]string{"xiaobi", "xiaoda", "xiaoji"} for index, item := range listArray {fmt.Printf("hello, %d, %s\n", index, item) } //java for (String item : someList) {System.out.println(item); }跳轉流程
Go很神奇的保留了一直被放棄的goto語句,記得是Basic、Pascal那些語言才會有,不知道為啥。
i := 1 flag: for i <= 10 {if i%2 == 1 {i++goto flag}fmt.Println(i)i++ }defer流程有點像Java里面的finally,保證了一定能執(zhí)行,我感覺底層也是goto的實現(xiàn)吧。在后面跟一個函數(shù)的調用,就能實現(xiàn)將這個xxx函數(shù)的調用延遲到當前函數(shù)執(zhí)行完后再執(zhí)行。
這是壓棧的變量快照實現(xiàn)。
func printName(name string) {fmt.Println(name) }func main() {name := "go"defer printName(name) // output: goname = "python"defer printName(name) // output: pythonname = "java"printName(name) // output: java }//output: java python go//defer后于return執(zhí)行 var name string = "go" func myfunc() string {defer func() {name = "python"}()fmt.Printf("myfunc 函數(shù)里的name:%s\n", name)return name } func main() {myname := myfunc()fmt.Printf("main 函數(shù)里的name: %s\n", name)fmt.Println("main 函數(shù)里的myname: ", myname) }//output: myfunc 函數(shù)里的name:go main 函數(shù)里的name: python main 函數(shù)里的myname: go4 函數(shù)
- 關鍵字是 func,Java則完全沒有 function 關鍵字,而是用 public、void 等等這樣的關鍵字,JS也可以用箭頭函數(shù)來去掉 function 關鍵字了。
- 函數(shù)的花括號強制要求在首行的末尾。
- 可以返回多個值!返回值的類型定義在參數(shù)后面了,而不是一開始定義函數(shù)就需要寫上,跟定義變量一樣,參數(shù)的類型定義也是一樣在后面的,如果相同則保留最右邊的類型,其他省略。
- 可以顯式聲明了返回值就可以了,必須每個返回值都顯式,就可以省略 return 變量。
匿名函數(shù)和閉包
在Java里面的實現(xiàn)一般是內部類、匿名對象,不能通過方法傳遞函數(shù)作為參數(shù),只能傳一個對象,實現(xiàn)接口。
Go則和JS一樣方便,可以傳遞函數(shù),定義匿名函數(shù)。
//傳遞匿名函數(shù) func main() {i := 10add := func (a, b int) {fmt.Printf("Variable i from main func: %d\n", i)fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)}callback(1, add); } func callback(x int, f func(int, int)) {f(x, 2) }//return 匿名函數(shù) func main() {f := addfunc(1)fmt.Println(f(2)) } func addfunc(a int) func(b int) int {return func(b int) int {return a + b} }不定參數(shù)
和Java類似,不同的是在調用是也需要用 ... 來標識。
//定義 func SkipHandler(c *gin.Context, skippers ...SkipperFunc) bool {for _, skipper := range skippers {if skipper(c) {return true}}return false }//調用 middlewares.SkipHandler(c, skippers...)五 面向對象
在C語言里面經常會有用到別名的用法,可以用 type 類起一個別名,很常用,特別是在看源碼的時候經常出現(xiàn):
type Integer int1 類
沒有 class 的定義,Go里面的類是用結構體來定義的。
type Student struct {id uintname stringmale boolscore float64 }//沒有構造函數(shù),但是可以用函數(shù)來創(chuàng)建實例對象,并且可以指定字段初始化,類似于Java里面的靜態(tài)工廠方法 func NewStudent(id uint, name string, male bool, score float64) *Student {return &Student{id, name, male, score} }func NewStudent2(id uint, name string, male bool, score float64) Student {return Student{id, name, male, score} }2 成員方法
定義類的成員函數(shù)方法比較隱式,方向是反的,不是聲明這個類有哪些成員方法,而是聲明這個函數(shù)是屬于哪個類的。聲明語法就是在 func 關鍵字之后,函數(shù)名之前,注意不要把Java的返回值定義給混淆了!
//這種聲明方式和C++一樣的,這個就是不是普通函數(shù)了,而是成員函數(shù)。 //注意到的是,兩個方法一個聲明的是地址,一個聲明的是結構體,兩個都能直接通過點操作。 func (s Student) GetName() string {return s.name } func (s *Student) SetName(name string) {s.name = name }//使用 func main() {//a是指針類型a := NewStudent(1, "aa", false, 45)a.SetName("aaa")fmt.Printf("a name:%s\n", a.GetName())b := NewStudent2(2, "bb", false, 55)b.SetName("bbb")fmt.Printf("b name:%s\n", b.GetName()) }//如果SetName方法和GetName方法歸屬于Student,而不是*Student的話,那么修改名字就會不成功 //本質上,聲明成員函數(shù),就是在非函數(shù)參數(shù)的地方來傳遞對象、指針、或者說是引用,也就是變相傳遞this指針 //所以才會出現(xiàn)修改名字不成功的case3 繼承
沒有 extend 關鍵字,也就沒有了繼承,只能通過組合的方式來實現(xiàn)。組合就解決了多繼承問題,而且多繼承的順序不同,內存結構也不同。
type Animal struct {name string } func (a Animal) FavorFood() string {return "FavorFood..." } func (a Animal) Call() string {return "Voice..." } type Dog struct {Animal } func (d Dog) Call() string {return "汪汪汪" }//第二種方式,在初始化就需要指定地址,其他都沒變化 type Dog2 struct { *Animal } func test() {d1 := Dog{}d1.name = "mydog"d2 := Dog2{}d2.name = "mydog2"//結構體是值類型,如果傳入值變量的話,實際上傳入的是結構體值的副本,對內存耗費更大,//所以傳入指針性能更好a := Animal{"ddog"}d3 := Dog{a}d4 := Dog2{&a} }這種語法并不是像Java里面的組合,使用成員變量,而是直接引用Animal并沒有定義變量名稱(當然也是可以的,不過沒必要了),然后就可以訪問Animal中的所有屬性和方法(如果兩個類不在同一個包中,只能訪問父類中首字母大寫的公共屬性和方法),還可以實現(xiàn)方法重寫。
4 接口
Java的接口是侵入式的,指的是實現(xiàn)類必須明確聲明自己實現(xiàn)了某個接口。帶來的問題就是,如果接口改了,實現(xiàn)類都必須改,所以以前總是會有一個抽象類在中間。
//定義接口: type Phone interface {call() } //實現(xiàn)接口: type IPhone struct {name string } func (phone IPhone) call() {fmt.Println("Iphone calling.") }Go的接口是非侵入式的,因為類與接口的實現(xiàn)關系不是通過顯式聲明,而是系統(tǒng)根據(jù)兩者的方法集合進行判斷。一個類必須實現(xiàn)接口所有的方法才算是實現(xiàn)了這個接口。接口之間的繼承和類的繼承一樣,通過組合實現(xiàn),多態(tài)的實現(xiàn)邏輯是一樣的,如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以賦值給接口A。
六 并發(fā)編程
目前并發(fā)編程方面還沒學習多少,就簡單從網上摘了這一個經典的生產者消費者模型例子來初步感受一下,后續(xù)深入學習過后再進行分享。
// 數(shù)據(jù)生產者 func producer(header string, channel chan<- string) {// 無限循環(huán), 不停地生產數(shù)據(jù)for {// 將隨機數(shù)和字符串格式化為字符串發(fā)送給通道channel <- fmt.Sprintf("%s: %v", header, rand.Int31())// 等待1秒time.Sleep(time.Second)} } // 數(shù)據(jù)消費者 func customer(channel <-chan string) {// 不停地獲取數(shù)據(jù)for {// 從通道中取出數(shù)據(jù), 此處會阻塞直到信道中返回數(shù)據(jù)message := <-channel// 打印數(shù)據(jù)fmt.Println(message)} } func main() {// 創(chuàng)建一個字符串類型的通道channel := make(chan string)// 創(chuàng)建producer()函數(shù)的并發(fā)goroutinego producer("cat", channel)go producer("dog", channel)// 數(shù)據(jù)消費函數(shù)customer(channel) }//output: dog: 1298498081 cat: 2019727887 cat: 1427131847 dog: 939984059 dog: 1474941318 cat: 911902081 cat: 140954425 dog: 336122540七 總結
這只是一個簡單入門,其實Go還有很多很多東西我沒有去涉及的,例如context、try-catch、并發(fā)相關(如鎖等)、Web開發(fā)相關的、數(shù)據(jù)庫相關的。以此貼開始,后續(xù)繼續(xù)學習Go語言分享。
原文鏈接
本文為阿里云原創(chuàng)內容,未經允許不得轉載。
總結
- 上一篇: GitHub Action + ACK:
- 下一篇: 读完《云原生架构白皮书》,我们来谈谈开放