65
Golang 入入入

Golang 入門初體驗

  • Upload
    -

  • View
    112

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Golang 入門初體驗

Golang 入門初體驗

Page 2: Golang 入門初體驗

學習主題• 社群成立目的簡介• 自我介紹與哈啦• 功力複習• 程式實作 (Golang)

• 問題與交流

Page 3: Golang 入門初體驗

個人簡介諸葛魔斌工作室:諸葛魔斌科技創作室學歷:高應大 --- 工管系 ( 非資訊科系 )E-Mail : [email protected]官方網站: http://twcts.comFacebook : https://goo.gl/volM7Z粉絲團 : https://goo.gl/LI08wh社群 : https://goo.gl/kjCsz1Line (id) : http://goo.gl/ayGW7dLine@ : http://goo.gl/pQHjWHLine Bot : http://goo.gl/ljdxfVPlay 商店: http://goo.gl/CkVdsdApp Store : http://apple.co/1Qehd64專長: VB(VBA) 、 C# 、 Java(Android) 、 Swift(iOS) 、 PHP 、 Python 、 Conrona 、 Golang 、 Unity 、 MySQL 、 FreeBSD 、 Server 架設、 CAD 、 ……

楊政斌

Page 4: Golang 入門初體驗

學習程式的歷程學生時期:打電動,沒有日夜的界限二專畢業:初次接觸 dBase 、 Lotus 使用 Clipper 做經銷存、應收付帳系統

工作階段 1 :接觸 Office ,開始使用 Excel(Lotus)

工作階段 2 :品保工作,自學圖表、函數、 VBA ,做出自動化品管系統興趣展開:自學 VB ,製作 ERP 系統,接觸 Linux 、 FreeBSD ,自學架設 Server

諸葛魔斌誕生

大學時期:半自學 Android App(Java) 、 C# 、 iOS App(Swift)

工作轉變:成立諸葛魔斌科技創作室,接案學習,救國團電腦資訊講師實績:

Page 5: Golang 入門初體驗

Golang 入門初體驗Go ,又稱 golang ,是 Google 開發的一種靜態強型別、編譯型,並發型,並具有垃圾回收功能的程式語言。羅伯特 · 格瑞史莫,羅勃 · 派克( Rob Pike )及肯 · 湯普遜於 2007 年 9 月開始設計 Go 語言,稍後 Ian Lance Taylor, Russ Cox 加入專案中。 Go 語言是基於 Inferno 作業系統所開發的。 Go 語言於 2009 年 11 月正式宣布推出,成為開放原始碼專案,並在 Linux 及 Mac OS X 平台上進行了實現,後追加 Windows 系統下的實作。Go 語言的語法接近 C 語言,但是對於變數的聲明是不同的,其他語法不同之處是 For 迴圈和 if 判斷語句不需要用小括弧括起來。 Go 語言支援垃圾回收功能。 Go 語言的並列模型是以東尼 · 霍爾的交談循序程式( CSP )為基礎,採取類似模型的其他語言套件括 Occam 和 Limbo ,但它也具有 Pi 運算的特徵,比如通道傳輸。與 C++ 相比, Go 語言並不包括如例外處理、繼承、泛型、斷言、虛擬函式等功能,但增加了slice 型、並行、管道、垃圾回收、介面( interface )等特性的語言級支援。當然, Google 對於泛型的態度還是很開放的,但在該語言的常見問題列表中,對於斷言的存在,則持負面態度,同時也為自己不提供型別繼承來辯護。不同於 Java , Go 語言內嵌了關聯陣列(也稱為雜湊表( hashes )或字典( dictionaries )),就像字串類型一樣。

Page 6: Golang 入門初體驗

Golang 入門初體驗當前有兩個 Go 語言的編譯器的分支。官方編譯器 gc 和 gccgo 。官方編譯器在初期使用 C 寫成,後用 go 重寫從而實作自舉。 Gccgo 是一個使用標準 GCC 作為後端的 Go 編譯器。官方編譯器支援跨平台編譯(但不支援 CGO ),允許將源碼編譯為可在目標系統、架構上執行的二進位檔案。Go 在 2007 年 9 月開始為 google 內部員工的 20% 自由時間的實驗項目 , 目的為改善公司內部的開發速度 . 隔了一年受到 google 公司的重視和支持 , 2009 年 11 月對外發佈第一版本 . 過去 google 常用的語言有 c , java , python 來開發他們的服務 . 但是這些語言是在創造的時候的硬體環境 , 和現在有很大的不同 . 而 Golang 在設計的時候就考慮了這些架構 , 所以在開發上和過去的語言比較起來有很大的優勢 .

Go 的關鍵字不多 ( 目前約 25 個 c 37 個 ,c++ 84 個而且還在增加中 ), 學習上很容易上手 , 再加上有不少和 c 的關鍵字重複 , 所以如果你原本就是寫 c/c++ 的人 , 在轉換上會相對其他語言容易 .

平均下來效能是輸 c, 可是贏其他語言如 ruby(ror), php, java, python 等語言 . 主要原因的先天優勢是 , 他語言架構設計非常單純 , 並不像是如物件導向語言這麼的大 . 再來是他不是直譯語言 . 光是這兩點優勢 , 讓他有更好的體質去有較好的執行效率 . 已經有很多案例 , 在開發伺服器的軟體時 , 因為效能問題而改換 go 去寫 , 結果讓他們減少了伺服器的數量就可以達到原本的效能 , 增加了服務的效率也省下了硬體成本 .

Page 7: Golang 入門初體驗

Golang 入門初體驗goroutine這是 Go 的最大特色之一 , thread 的使用是非常消耗系統資源的 , 而且當你使用越來越多時管理上也越困難 . 而 goroutine 有輕巧低消耗資源的特性 , 而這優點在於系統資源的消耗也會比較少 .

可以使用 c library他可以使用 c library, 解決新語言在 library 上的不足 . 不過基本上 go 本身的 lib 已經很豐富了 . Unicode slice map 都有支援 .目前越來越多公司用 go 來寫他們網站後端的系統 , 過去 google 在寫他的服務大多使用 c + python, python 是個很簡潔的語言在建構服務是非常快的 , c 是用來解決效能上的問題 , 現在內部也都慢慢轉成 go 來寫 . 在 2014 年 TIOBE四月程式語言排名已經到第 30名 , 2013 基本上都是在 50名以外 , 而且我相信這名次會持續在進步 .

第一個 go 程式package mainimport "fmt“func main () { fmt.Println("Hello , World!")}

Page 8: Golang 入門初體驗

Golang 入門初體驗Go 安裝http://golang.org/dl/

Windows:安裝好 GO 後 , 還需要 GCC 編譯環境 , 因為 Mac 的 Xcode 本身就有 GCC, windows 沒有 , 所以選擇了 mingw 這個輕量型的 GCChttp://blog.jex.tw/blog/2013/12/17/windows-install-gcc-compiler-mingw/

設定 GOPATH, 這是放一些你自己或 go get from github 存放的地方1. 在 c:\Go 下建立 mygo 資料夾 , 並且在 mygo 下建立三個資料夾 src, pkg, bin2. 設定環境變數 GOROOT : C:\Go GOPATH : C:\Go\mygo PATH : 在最後面加上 ;%GOROOT%\bin;%GOPATH%\bin;C:\Program Files (x86)\Git\bin除了加上 GOROOT\bin 、 GOPATH\bin, 也可視需求加上 git bin 的路徑,重新啟動 cmd 使變數生效

Page 9: Golang 入門初體驗

Golang 入門初體驗Go 執行https://golang.org/cmd/go/

go run

要撰寫第一個 Hello, World 程式,你可以建立一個 main.go ,在當中撰寫以下的內容:package main

import "fmt"

func main() { fmt.Println("Hello, World") fmt.Println("哈囉!世界! ")}

Page 10: Golang 入門初體驗

Golang 入門初體驗Go 執行每個 .go 原始碼,都必須從 package 定義開始,而對於包括程式進入點 main 函式的 .go 原始碼,必須是在 package main 之中,為了要能輸出訊息,這邊使用了 fmt 套件( package )之中的 Println 函式,開頭的大寫 P 表示這是個公開的函式,可以在套件之外進行呼叫。Go 的創建者之一也是 UTF-8 的創建者,因此, Go 可以直接處理多國語言,只要你確定編輯器編碼為 UTF-8 就可以了,如果你使用 vim ,可以在 vim 的命令模式下輸入 :set encoding=utf-8 ,或者是在 .vimrc 之中增加一行 set encoding=utf-8 。Go 可以用直譯的方式來執行程式,第一個 Hello, World 程式就是這麼做的,執行 go run 指定你的原始碼檔名就可以了:$ go run main.goHello, World哈囉!世界!package 與 GOPATH

Page 11: Golang 入門初體驗

Golang 入門初體驗Go 執行那麼,一開始的 package 是怎麼回事?試著先來建立一個 hello.go:package hello

import "fmt"

func HelloWorld() { fmt.Println("Hello, World")}

Page 12: Golang 入門初體驗

Golang 入門初體驗Go 執行記得, package 中定義的函式,名稱必須是以大寫開頭,其他套件外的程式,才能進行呼叫,若函式名稱是小寫,那麼會是套件中才可以使用的函式。接著,原本的 main.go 修改為:package main

import "hello"

func main() { hello.HelloWorld()}

Page 13: Golang 入門初體驗

Golang 入門初體驗Go 執行現在顯然地, main.go 中要用到方才建立的 hello 套件中的 HelloWorld 函式,這時 package 的設定就會發揮一下效用,你得將 hello.go 移到 src/hello 目錄之中,也就是目錄名稱必須符合 package 設定之名稱。而 src/hello 的位置,必須是在 GOROOT 底下,或者是 GOPATH 底下,當 Go 需要某個某個套件中的元素時,會分別到這兩個環境變數的目錄之中,查看是否有相應於套件的原始碼存在。為了方便,通常會設定 GOPATH ,例如,指向目前的工作目錄:export GOPATH=~\workspace\go雖然目前 GOPATH 中只一個目錄,不過 GOPATH 中可以設定數個目錄

Page 14: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

這個命令主要用於測試編譯。在包的編譯過程中,在必要情況下,還可以同時編譯與之關聯的包。- 普通包:執行完 go build , 不會產生任何檔,如果需要在 $GOPATH/pkg 下生成相應檔,則要執行 go install.

-main 包:執行完 go build,會在目前的目錄下生成一個可執行檔,如果需要在 $GOPATH/bin 下生成對應檔,需要執行 go install 或使用 go build -o outputpath/

如果只想編譯某個檔,只需在在後面加上檔案名即可,例如 :go build hello.go

非main 包在預設情況下編譯輸出的是 package名, main 包是第一個原始檔案的檔包,也還可以指定編譯輸出的檔案名,例如, go build -o xialingsc.exe

Page 15: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

go build 會忽略目錄下以 "_" 或 "." 開頭的 go文件如果原始程式碼針對不同的作業系統需要不同的處理,那麼可以根據不同的作業系統尾碼來命名檔。例如, readfile_linux.go,readfile_drawin.go,readfile_windows.go.go build 會選擇性編譯檔, Linux 系統只編譯 readfile_linux.go ,其他文件則被忽略。

Page 16: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

如果想編譯原始碼為可執行檔,那麼可以使用 go build ,例如,直接 go build main.go ,就會在執行指令的目錄下,產生一個名稱為 main 的可執行檔,可執行檔的名稱是來自己指定的原始碼檔案主檔名,執行產生出來的可執行檔就會顯示 Hello, World 。你也可以建立一個 bin 目錄,然後執行 go build -o bin/main main.go ,這樣產生出來的可執行檔,就會被放在 bin 底下,如果想將原始碼全部放在 src 底下管理,那麼就將 main.go 放到 src/main 底下,然後執行 go build -o bin/main src/main/main.go 。go install

每次使用 go build ,都是從原始碼編譯為可執行檔,這比較沒有效率,如果想要編譯時更有效率一些,可以使用 go install ,例如,在目前既有的目錄與原始碼架構之下,執行 go install main

Page 17: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

~go |~bin/ | |-main |~pkg/ | `~linux_arm/ | `-hello.a |~src/ | |~hello/ | | `-hello.go | |~main/ | | `-main.go

go install packageName 表示要安裝指定名稱的套件,如果是 main 套件,那麼會在 bin 中產生可執行檔,如果是公用套件,那麼會在 pkg 目錄的 $GOOS_$GOARCH 目錄中產生 .a 檔案

Page 18: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

可以使用 go env 來查看 Go 使用到的環境變數,例如:$ go envGOARCH="arm"GOBIN=""GOEXE=""GOHOSTARCH="arm"GOHOSTOS="linux"GOOS="linux"GOPATH="/root/workspace/go"GORACE=""GOROOT="/opt/go"GOTOOLDIR="/opt/go/pkg/tool/linux_arm"GO15VENDOREXPERIMENT=""CC="gcc"GOGCCFLAGS="-fPIC -marm -pthread -fmessage-length=0"CXX="g++"CGO_ENABLED="1"

Page 19: Golang 入門初體驗

Golang 入門初體驗Go 執行go build

.a 檔案是編譯過後的套件,因此,你看到的 hello.a ,就是 hello.go 編譯之後的結果,如果編譯時需要某個套件,而對應的 .a 檔案存在,且原始碼自上次編譯後未曾經過修改,那麼就會直接使用 .a 檔案,而不是從原始碼開始編譯起。

go clean用來移除當前源碼包裡編譯生成的檔,這些檔包括, _obj(舊的 objects 目錄, Makefiles遺留 ) ,_test(舊的 test 目錄 ) , _testmain.go (舊的 gotest文件), test.out 、 build.out (舊的test記錄)*.[568ao] object文件 ,DIR ( .exe ) ( 由 go build產生 ) , DIR.test ( .ext)( 由 go test -c產生 ) ,MAINFILE(.exe) ( 由 go build MAINFILE.go產生 )該命令最大的作用,清除編譯文件後,上傳 git ,保持源碼清潔。

Page 20: Golang 入門初體驗

Golang 入門初體驗Go 執行os.Args

那麼,如果想在執行 Go 程式時使用命令列引數呢?可以使用 os 套件的 Args ,例如,寫一個 main.go:package main

import "os"import "fmt"

func main() { fmt.Printf("Command: %s\n", os.Args[0]) fmt.Printf("Hello, %s\n", os.Args[1])}

Page 21: Golang 入門初體驗

Golang 入門初體驗Go 執行os.Args 是個陣列,索引從 0 開始,索引 0 會是編譯後的可執行檔名稱,索引 1 開始會是你提供的引數,例如,在執行過 go build 或 go install 之後,如下直接執行編譯出來的執行檔,會產生的訊息是…$ ./bin/main JustinCommand: ./bin/mainHello, Justingo doc

Page 22: Golang 入門初體驗

Golang 入門初體驗Go 執行fmt 的 Printf ,就像是 C 的 printf ,可用的格式控制字元可參考 Package fmt 的說明。實際上, Go 本身附帶了說明文件,可以執行 go doc <pkg> <sym>[.<method>] 來查詢說明。例如:$ go doc fmt.Printffunc Printf(format string, a ...interface{}) (n int, err error)

Printf formats according to a format specifier and writes to standard output. It returns the number of bytes written and any write error encountered.

Page 23: Golang 入門初體驗

Golang 入門初體驗Go 執行go fmt幫助格式化寫好的代碼檔,讓寫代碼時不關心格式,寫完後,輕鬆執行 go fmt 檔案名 .go 就好提高效率。更多的時候可以採用 gofmt, 同時增加 -w 的參數,否則格式化結果不會寫入檔,例如:gofmt -w src 來格式整個項目。

go get動態獲取遠端代碼。這個代碼內部分為兩部分,一是下載源碼包,另一步是執行 go install為了讓 go get 正常使用,需保證安裝了合適的源碼管理工具,並將這些命令加入到 PATH 中。可通過 go help remote 瞭解更多。

go test執行這個命令會自動讀取源碼目錄下名為 *_test.go 檔,生成並運行測試用的可執行檔。預設情況下不需要任何參數,也可帶上參數,具體可參見 go help testflag

Page 24: Golang 入門初體驗

Golang 入門初體驗Go 執行go doc如何查看相應的 package文檔呢?如果是 builtin 包,可以執行 go doc builtin; 如果是 http 包,執行 go doc net/http;查看某個包裡面的函數,類似執行 godoc fmt Println, 還可以查看相應代碼godoc -src fmt Println很棒的一點是,可以在終端執行 godoc -http=:埠號,例如 godoc -http=:8080 , 就可以在流覽器中敲入 127.0.0.1:8080 進行文檔內容的查看。其他命令go fix 用來修復以前老版本的代碼到新版本go version 查看 go 當前的版本go env 查看當前 go 的環境變數go list 列出當前全部安裝的 packagego run 編譯並運行 go 語言程式

Page 25: Golang 入門初體驗

Golang 入門初體驗Go IDEs

IDEs 有以下幾種,使用文字編輯器亦可完成程式編輯,再至 Command Line 執行編譯Lite IDE

GoSublime

Visual Studio Code

Goclipse

VIM / VIM-go

Page 26: Golang 入門初體驗

Golang 入門初體驗Go 語法 (參考 https://polor10101.gitbooks.io/golang_note/content/about_golang.html)

variable 變數雖然號稱和 c 類似 , 不過 go 在變數的命名上有很大的不同 , 他是以 type 放後面為原則以下表示宣告一個變數 x 且他是整數型態 .var x int

如果要給他初始值 , 有以下幾種方式給予初始值var x int = 10var y = 20

var z int z = 30

k := 40

常數const PI = 3.1415const NAME = "Nelson"常數不需要給定 type, 只要直接給予值即可 .

而且不能用 ':=' 的方式給予初始值 .

你可以發現每行的結尾少了分號 ';', 是的 go 省略分號 , 只要斷行來分就好 .

Page 27: Golang 入門初體驗

Golang 入門初體驗Go 語法函式函式的宣告方式也和 c 不同func add( a int , b int){ c := a + b}如果要回傳東西func add( a int , b int) int{ c := a + b return c}但是還是維持 type 在後面的原則在此在大括號的位置也有規定 , 如例子所示

Page 28: Golang 入門初體驗

Golang 入門初體驗Go 語法函式你也可以func add( a int , b int) int{ }但是不可以採用對齊的方式func add( a int , b int) int{

}從以上例子來看 , 會發現 go 在程式的風格上有強制規定 , 不符合規定會有 error

Page 29: Golang 入門初體驗

Golang 入門初體驗Go 語法函式有幾點初學的時候有點不習慣每行 code 不需要分號 ;大括號的排版方式import 某個 lib 或是宣告某變數但是實際卻沒有使用宣告改成這個方式 , 官方是說可以增加閱讀性 . why? 這部份在其他章節會有描述另外可以用變數直接指向 function

func add(a int,b int) int{ return a+b}

Page 30: Golang 入門初體驗

Golang 入門初體驗Go 語法關於語法go 在語法中做了改變 , 官方的說法是 , 改這樣的原因是因為這樣比較好閱讀 . 接著來比較 c 和 go 的語法以宣告 variable 來看int add; //cvar add int //gogo 還多一個 var, 似乎也沒有比較方便以 function 來看int add(int a, intb); //cfunc add(a int, b int)int //go也還好 , 不過比較起來 go 比較容易分辨出是 variable 還是 function, 但是差距也沒有很大 , 而且 go 還需要多打字 .

Page 31: Golang 入門初體驗

Golang 入門初體驗Go 語法關於語法如果宣告一個 function pointer 然後輸入一個 function pointer 變數int (*fp)(int (*ff)(int x, int y), int b) //cf func(func(int,int) int, int) int //goc 已經有點讓人混亂了 , 且不好閱讀 .

go 這樣是不是有比較清楚?個人是覺得有 , 還很明顯 .

再加個回傳 function pointer 的宣告比較 .

int (*(*fp)(int (*)(int, int), int))(int, int) //c f func(func(int,int) int, int) func(int, int) int //go結論 : 真的有差 .f := addf(10,20)

Page 32: Golang 入門初體驗

Golang 入門初體驗Go 語法loop 迴圈以 c 來說迴圈有 for , while , do while 三個關鍵字 . 在 go 裡面只有一個 for 來達到全部以 for 迴圈來說 c 為int i = 0;int sum = 0;for( i = 0 ; i < 10 ; ++i ){ sum += i;}以下為 go 的 for 迴圈範例sum := 0for i := 0 ; i < 10 ; ++i { sum += i}從範例中可以看到 "( )" 省略了 , 再來兩個中夸號並不是對齊的

Page 33: Golang 入門初體驗

Golang 入門初體驗Go 語法loop 迴圈c 的 do while 迴圈int i = 0;

do{ i += 1}while( i < 10 );

go 中為i := 0for i < 100 { i += 1}

c 的無限 while 迴圈while(1){

}

go 的for {

}以上為 go 和 c 的迴圈使用比較 . 從中可以了解一些語法上的差別

Page 34: Golang 入門初體驗

Golang 入門初體驗Go 語法if switch和 c 不同的是 , if 不需要小括號 , 另外大括號必須要有 , 並且強制是範例的排版方式 , 否則會有 error

if a > 0 { fmt.Println(a)}個人是習慣對齊的方法 , 不過這樣的強制規定可以讓多人開發專案的程式碼有一致性 , 這也是個好處 .

switch 也是同樣的規定 , 並且不需要加上 'break'

如以下所示switch i {case 1 : fmt.Println(1)case 2 : fmt.Println(2)default : fmt.Println("default")}

Page 35: Golang 入門初體驗

Golang 入門初體驗Go 語法Array

array 的宣告 var a [5]int // 宣告一個大小 5 的 int array var b []int // 宣告空的 int array var c = []int{1,2,3,4,5}// 宣告一個大小 5 的 int array, 並給初始值如果要列印 array 裡面的值 , 你可以使用 range 知道 array 目前的 index

import "fmt"

func main() { var a = [4]int {1,2,3,4}

for i := range a { fmt.Println(a[i]) }}

Page 36: Golang 入門初體驗

Golang 入門初體驗Go 語法Array

初始話也可以改成var a = [4]int { 1, 2, 3, 4,}

其中 4 的後面記得要有逗號 ','

Page 37: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

相對於 array, slice 提供了彈性 , 不需要指定大小是由三個元件組成ptrlencap其中 prt 是指向一個 array 的指標 , 如圖所示

Page 38: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

len 為實際資料長度 , cap 表示容量宣告的方式為 a := []int此時的 len , cap 都為 0

你可以用 make 來宣告變數並指定 len 和 cap 的值a := make([]int,5,10)其中 5 為 len, 10 為 cap

Page 39: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

也可以不指定 cap 寫成a := make([]int,5)這時 len 為 5 , cap 為 5

另外給定初始值的宣告var a = []int {1,2,3,4,5}如果要一個變數指向 a

b := a[2:4]c := a[:]

Page 40: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

其中b 為 3 , 4.

a[2:4] 的 2 表示指向 a index = 2 的位置 , 4 表示為結尾在 index = 4( 不包含 ), 所以則 length 為 2, 另外由於 array 是連續的 , 所以 cap = 3

c 為 1,2,3,4,5[:] 表示 [0:len(a)], 第一個值如果沒寫則為 0, 第二個沒寫的為 len(a) = 5

Page 41: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

所以可以得知a[:3] 和 a[1:] 個別為1,2,3 //a[:3]2,3,4,5 //a[1:]另外要特別注意的是 ,

b := a[1:3]並不是 a copy 值給 b, 而是將 b 的指標指向 a ( index 2 ) 的位置所以這表示如果你改了 b 的值 , a 的也會一起改變 , 因為他們共用同一個 array.如果你要兩者是獨立的 , 可以使用 copy 的方法copy(b,c)

Page 42: Golang 入門初體驗

Golang 入門初體驗Go 語法Slice

這是把 c 的前兩個的值 , copy 到 b 中 . 為什麼只 copy c 的前兩個?因為 b 的大小只有二 , 以小的為主copy(c,b)則表示將 b 的值 , copy 覆蓋 c 的前兩個值 .另外可以用 append 來增加他的大小如下所示import "fmt"

func main(){ var a[]int a = append(a,1) a = append(a,2)

for i := range a { fmt.Println(a[i]) }}

Page 43: Golang 入門初體驗

Golang 入門初體驗Go 語法Map

建立一個 map 可以用以下兩種方式m := make(map[string]int)m := map[string]int{}定義 age = 16

m["age"] = 16回傳值 , 和 map 是否存在value , ok := m["age"]如果是不存在的 key , value = 0 , ok = false

value , ok := m["height"]

Page 44: Golang 入門初體驗

Golang 入門初體驗Go 語法Map

利用 rage 取得 map 中所有的 key 和 value, 並列印for key, value := range m { fmt.Println("Key:", key, "Value:", value) }

如果要一次宣告多個值 , 可以用以下方式person := map[string]int{ "age" : 16, "height" : 180, "weight" : 6, }

https://play.golang.org/p/rPpLGhc9lE

Page 45: Golang 入門初體驗

Golang 入門初體驗Go 語法Goroutine

在講 goroutine 之前 , 必須先了解 concurrency 和 parllelism 的不同concurrency (併發 ) vs parallelism ( 並行 )

併發 concurrency 是將 process 切割成多個可執行單位 (coroutine), 如果有多個 process 時則在 coroutine 中交互執行 , 這樣不會因為某一個 process 執行太久造成其他 process 無法處理 , 會讓你有多工的感覺 . 但這並不會增加執行效率 . 總執行時間是一樣的 , 如圖一所示 . 另外和 thread 不同的是 , thread 是屬於 os 層面上的 , 且 thread 非常消耗系統資源 .

Page 46: Golang 入門初體驗

Golang 入門初體驗Go 語法Goroutine

Page 47: Golang 入門初體驗

Golang 入門初體驗Go 語法Goroutine

並行 parallelism 是多個 process 由多個 cpu 去執行 , 可以增加執行的效率 , 縮短總執行的時間 . 如圖二所示

Page 48: Golang 入門初體驗

Golang 入門初體驗Go 語法Goroutine

go 用 goroutine 實現了 concurrency. 使用者只要設定最大的 thread 數量 , 和系統是多少 CPU, run-time 就會幫你處理好剩下的 thread 管理 , 但為什麼 gorountine 可以提供較好的效能 ? ( 以下為個人心得可能有錯)

Page 49: Golang 入門初體驗

Golang 入門初體驗Go 語法Goroutine

第一 . 在創造 goroutine 的資源消耗是很小的 , 因為 goroutine 只是一個 function 的入口 . 只要很小的 stack 去記錄即可 .

第二 . thread 上的管理 , 當某個 goroutine 被 block 住 , 處理此 goroutine 的 thread 就耗在這上 , 此時 run-time 會將其他的 goroutine 轉給此 thread 去執行 , 這樣 thread 就不會因為執行某些被 block 的任務則消耗在那邊 . 有效的使用 thread.

第三 . thread 最多不會超過 $GOMAXPROCS( 使用者設定的 ), 可以確保 thread 不會失控的增加 .

綜合以上三點 , goroutine 其實就是避免 thread 的不當增加 , 有效的使用目前的 thread , 這樣系統資源的消耗就少了 . 不會因為系統的資源不夠造成整體效能的降低 .coroutine 資料一coroutine 資料二thread 資料

goroutine 資料一goroutine 資料二goroutine 資料三

Page 50: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine - channelsDon't communicate by sharing memory; share memory by communicating.不同於過去的 muliti thread 的程式開發 , 常使用共用變數去做資訊傳遞 (communicate by sharing memory).

goroutine 彼此的溝通方式是使用 channel (share memory by communicating). 這樣的方式會讓整個流程在表現上變得清楚 .

channelschannel 的通訊 , 需要一個 sender 和一個 receiver, 當 sender 和 receiver 都 ready 時候 , 訊息才會成功的傳遞 .

import "fmt"

func main(){ s := mack(chan string) // 宣告一個 channel 變數 s <- "hello" // 寫入 channel (sender) val <- s //讀取 channel (receiver) fmt.Println(val)}上例說明了如何宣告 channel 變數 , 寫入 channel, 讀取 channel.

Page 51: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine - channels但是 code 無法跑 , compiler 會顯示 dead lock.

因為執行到 s <- "hello" 這步的時候 , sender 就會進入 ready 狀態 .

然後就停住了 , 也就是他不會執行到 val := <- s .這時我們建立一個 goroutine 去跑 s <- "hello"

import "fmt"

func main(){ s := make(chan string) // 宣告一個 channel 變數 go func(){ s <- "hello" // 寫入 channel (sender) }()

val := <- s //讀取 channel (receiver) fmt.Println(val)}

Page 52: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine - channels執行順序就是1. 建立一個 goroutine //此時 s <- "hello" 還沒執行2. 執行 val := <- s //s 空的 , receiver ready (停住 )3. 執行 s <- "hello" //sender 將訊息寫入 s, sender ready4. val := <- s // 成功讀取 s, (因為 receiver, sender 都 ready)5. fmt.Println(val)goroutine 不見得會比較慢執行 . 不過重點不在這 , 就不多描述了 .

所以要看執行的順序就要注意這原則 " 當 sender 和 receiver 都 ready 時候 , 訊息才會成功的傳遞 . "

再舉一個例子 , 請問順序式如何?

Page 53: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine – channels

import "fmt"

func main(){ s := make(chan string) go func() { for i := 0; i < 3; i++ { fmt.Println("sender hello",i) s <- fmt.Sprintf("receiver hello %d", i) } }()

for i := 0; i < 3; i++ { val := <-s fmt.Println(val) }}

Page 54: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine – channels

結果sender hello 0sender hello 1receiver hello 0receiver hello 1sender hello 2receiver hello 2

有對嗎?程式碼順序是這樣1. 建立一個 goroutine 2. 執行 val := <- s //s 空的 , receiver ready (停住 )[sender hello 0]3. 執行 s <- fmt.Sprintf.. //sender 將訊息寫入 s, 傳訊成功[sender hello 1]4. 執行 s <- fmt.Sprintf.. //s 有值 , sender ready(停住 )5. val := <-s //讀到 s 的值[receiver hello 0]6. val := <-s //s 空的 , receiver ready, 傳訊成功[receiver hello 1]7. val := <-s //s 空的 , receiver ready (停住 )[sender hello 2]8. 執行 s <- fmt.Sprintf.. //sender 將訊息寫入 s, 傳訊成功9. val := <-s //讀到 s 的值[sender hello 2]

http://guzalexander.com/2013/12/06/golang-channels-tutorial.html

Page 55: Golang 入門初體驗

Golang 入門初體驗Go 語法goroutine – channels

另外我們可以修改 channel 的大小為 2

import "fmt"func main(){ s := make(chan string,2) go func() { for i := 0; i < 3; i++ { fmt.Println("sender hello",i) s <- fmt.Sprintf("receiver hello %d", i) } }()

for i := 0; i < 3; i++ { val := <-s fmt.Println(val) }}

結果sender hello 0sender hello 1sender hello 2receiver hello 0receiver hello 1receiver hello 2

http://guzalexander.com/2013/12/06/golang-channels-tutorial.html

Page 56: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

defer

先看以下範例func f(){ for i := 0 ; i < 5 ; i++{ fmt.Println(i) } fmt.Println("f finish")}

此時的結果是01234f finish

Page 57: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

如果再 fmt.Println 前面加上 defer

func f(){ for i := 0 ; i < 5 ; i++{ defer fmt.Println(i) } fmt.Println("f finish")}結果就變成f finish43210

Page 58: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

defer 是讓 fmt.Println(0) , fmt.Println(1) , fmt.Println(2) , fmt.Println(3) , fmt.Println(4) 依序放到清單中 , 等到 func f 結束前 , 再依據 Last-In-First-Out (LIFO) 的順序 call 清單中的 function.

這樣功能提供了什麼的好處?我們再看以下的例子

Page 59: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return }

dst, err := os.Create(dstName) if err != nil { return }

written, err = io.Copy(dst, src) dst.Close() src.Close() return}

Page 60: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

這個例子有個 bug, 就是當 os.Create(dstName) 失敗的時候 function 就會 return, 而 src.Close() 就沒有執行到了 .

所以可以改成這樣

Page 61: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return }

dst, err := os.Create(dstName) if err != nil { src.Close() return }

written, err = io.Copy(dst, src) dst.Close() src.Close() return}

Page 62: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

但是如果開多個檔案 ? 那會很麻煩所以我們利用 defer 來修改func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close()

dst, err := os.Create(dstName) if err != nil { return } defer dst.Close()

return io.Copy(dst, src)}

這時不管如何 , 都會執行 close function.

Page 63: Golang 入門初體驗

Golang 入門初體驗Go 語法defer panic recover

但是如果開多個檔案 ? 那會很麻煩所以我們利用 defer 來修改func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close()

dst, err := os.Create(dstName) if err != nil { return } defer dst.Close()

return io.Copy(dst, src)}

這時不管如何 , 都會執行 close function.

參考網站https://blog.golang.org/defer-panic-and-recover

panic recovergo 沒有 try catch , 原因是 go 的開發者認為 , 當過多 try catch 時會造成程式碼很難閱讀 . 所以提供了 panic recover 優雅的解決這個問題 .

Page 64: Golang 入門初體驗

Golang 入門初體驗Go 後續

GoWeb

Go Bot (Line 、 Facebook Message 、 Slack 、 Telegram 、 …… ..)

Go 機器學習Go 物聯網

Page 65: Golang 入門初體驗

Golang 入門初體驗