by sheepbao
主要大概介紹go語言的歷史和特性,簡單的入門。
來歷
很久以前,有一個IT公司,這公司有個傳統,允許員工擁有20%自由時間來開發實驗性項目。在2007的某一天,公司的幾個大牛,正在用c++開發一些比較繁瑣但是核心的工作,主要包括龐大的分布式集群,大牛覺得很鬧心,后來c++委員會來他們公司演講,說c++將要添加大概35種新特性。這幾個大牛的其中一個人,名為:Rob Pike,聽后心中一萬個xxx飄過,“c++特性還不夠多嗎?簡化c++應該更有成就感吧”。於是乎,Rob Pike和其他幾個大牛討論了一下,怎么解決這個問題,過了一會,Rob Pike說要不我們自己搞個語言吧,名字叫“go”,非常簡短,容易拼寫。其他幾位大牛就說好啊,然后他們找了塊白板,在上面寫下希望能有哪些功能(詳見文尾)。接下來的時間里,大牛們開心的討論設計這門語言的特性,經過漫長的歲月,他們決定,以c語言為原型,以及借鑒其他語言的一些特性,來解放程序員,解放自己,然后在2009年,go語言誕生。
思想
Less can be more
大道至簡,小而蘊真
讓事情變得復雜很容易,讓事情變得簡單才難
深刻的工程文化
優點
- 自帶gc。
- 靜態編譯,編譯好后,扔服務器直接運行。
- 簡單的思想,沒有繼承,多態,類等。
- 豐富的庫和詳細的開發文檔。
- 語法層支持並發,和擁有同步並發的channel類型,使並發開發變得非常方便。
- 簡潔的語法,提高開發效率,同時提高代碼的閱讀性和可維護性。
- 超級簡單的交叉編譯,僅需更改環境變量。(花了我兩天時間編譯一個imagemagick到arm平台)
- 內含完善、全面的軟件工程工具。Go語言自帶的命令和工具相當地強大。通過它們,我們可以很輕松地完成Go語言程序的獲取、編譯、測試、安裝、運行、運行分析等一系列工作,這幾乎涉及了開發和維護一個軟件的所有環節。
hello
package main func main() { println("hello, sewise") }
type
主要講講特有的類型,其他基本類型不做介紹
slice
切片:可以理解為動態數組,類似c++的vector 聲明一個slice
var slice []T // 如: var ints []int
slice的追加
ints = append(ints, 1) ints = append(ints, 2, 3, 4, 5)
slice的截取
newInts1 := ints[2:3] newInts2 := ints[2:] newInts3 := ints[:4]
map
字典:鍵值對
var json map[string]string
interface
接口:方法的集合,是一種合約 栗子: 聲明一個bird接口
var bird interface { fly() }
聲明一個hen對象(實現bird接口)
type hen struct { weight int hasFeather bool } func (h hen) fly() { fmt.Println("iI can fly") } func (h hen) eat() { h.weight++ fmt.Println("yes, I can eat") }
聲明一個 pig 對象(未實現 bird 接口,因為 pig 不會 fly)
type pig struct { age int weignt int hasFeather bool } func (p pig) run() { fmt.Println("I can run") } func (p pig) eat() { p.weight += 100 fmt.Println("Yes, I can eat so much") } func (p pig)sleep(){ fmt.Println("I slept all my life") } // pig can't fly
channel
通道:輕量集隊列,傳遞某種類型的值的通道
var ch chan int ch = make(chan int, 1)
往ch寫入一個數據
ch<- 8888
從ch讀取數據
out := <-ch
特性: channel是有長度的,當channel的緩沖為滿時,再往里寫入就會阻塞,當channel的緩沖為空時,從channel讀就會阻塞
package main import ( "fmt" "time" ) func main() { ch := make(chan int) fmt.Println("ch len:", len(ch)) go func() { // 往緩沖滿的channel里寫數據(阻塞) // ch <- 1 // 從緩沖為空的channel里讀數據(阻塞) <-ch fmt.Println("I am in minor goroutine") }() fmt.Println("I am in main goroutine") time.Sleep(2 * time.Second) }
當長度為 0 是,就是不帶緩沖的 channel 長度大於0,就是帶緩沖的 channel
並發
關鍵字:go 啟動 go 程 一個普通的函數或方法調用前加上關鍵字 go,即可啟動一個 goroutine
go func() { fmt.Println("start func") time.Sleep(120*time.Second) }()
競爭條件檢測 -race race.go
package main import ( "fmt" "time" ) func main() { a := 1 go func() { a = 2 }() a = 3 fmt.Println("a is ", a) time.Sleep(2 * time.Second) }
檢測:執行go run -race race.go
a is 3 ================== WARNING: DATA RACE Write at 0x00c420072188 by goroutine 6: main.main.func1() /Users/bao/program/go/gowork/hello/src/research/race.go:11 +0x3b Previous write at 0x00c420072188 by main goroutine: main.main() /Users/bao/program/go/gowork/hello/src/research/race.go:13 +0x9f Goroutine 6 (running) created at: main.main() /Users/bao/program/go/gowork/hello/src/research/race.go:12 +0x8e ================== Found 1 data race(s)
結果分析: goroutine6運行到第11行和main goroutine運行到13行的時候觸發競爭了。而且goroutine6是在第12行的時候產生的。
package
包的管理,關鍵字import,GOPATH
gopath
gopath是一個路徑列表,存放go項目工程 GOPATH文件目錄結構
├── bin 二進制文件目錄 ├── pkg 編譯好的庫文件目錄 └── src 源碼目錄
平常項目的目錄結構
├── bin 二進制文件目錄 ├── pkg 編譯好的庫文件目錄 └── src 源碼目錄 ├── main 入口函數目錄 └── vendor 當前項目的庫目錄 └── sheepbao.com └── glog
import
比如上面的項目,我要在main.go引入glog包
package main // 引入glog包 import "sheepbao.com/glog" func main() { glog.Println("test") }
go的工程工具簡介
test
go的命令工具 test,用來做測試
單元測試
go test 只測試函數名被它正確匹配的測試函數 go test -v -run="French|Canal"
栗子: add.go
package test func addOne(i int) int { return i + 1 }
add_test.go
package test import "testing" func TestAddOne(t *testing.T) { result := addOne(1) if result != 2 { t.Error("1+1!=2") } }
bao@baoMac test$ go test -v . === RUN TestAddOne --- PASS: TestAddOne (0.00s) PASS ok _/Users/bao/program/go/gowork/hello/src/research/test 0.006s
基准測試
go test -bench=. 內存的分配情況 go test -bench=FuncName -benchmem 栗子: stringsCon.go
package bench import ( "fmt" ) func Sprintf(s1, s2, s3 string) string { return fmt.Sprintf("%s%s%s", s1, s2, s3) } func AddString(s1, s2, s3 string) string { return s1 + s2 + s3 }
stringCon_test.go
package bench import "testing" var ( s1 = "make it run!" s2 = "make it right!" s3 = "make it fast!" ) func BenchmarkSprintf(b *testing.B) { for i := 0; i < b.N; i++ { Sprintf(s1, s2, s3) } } func BenchmarkAddString(b *testing.B) { for i := 0; i < b.N; i++ { AddString(s1, s2, s3) } }
go test -bench=.
bao@baoMac bench$ go test -bench=. testing: warning: no tests to run BenchmarkSprintf-4 5000000 349 ns/op BenchmarkAddString-4 20000000 61.7 ns/op PASS ok _/Users/bao/program/go/gowork/hello/src/research/bench 3.414s
樣本測試
package et import ( "fmt" ) func ExampleHello() { fmt.Println("hello, sewise") // Output: hello, sewise }
bao@baoMac example$ go test -v . === RUN ExampleHello --- PASS: ExampleHello (0.00s) PASS ok _/Users/bao/program/go/gowork/hello/src/research/example 0.006s
如果把上面的// Output: hello, sewise改為// Output: hello, sewis 則樣本測試不能通過
bao@baoMac example$ go test -v . === RUN ExampleHello --- FAIL: ExampleHello (0.00s) got: hello, sewise want: hello, sewis FAIL exit status 1 FAIL _/Users/bao/program/go/gowork/hello/src/research/example 0.006s
pprof
go的命令工具 pprof,用來性能分析(內存使用,泄露和cpu消耗) go tool pprof
- 查看cpu使用: go tool pprof http://localhost:8089/debug/pprof/profile 終端查看:
可視化(原是svg文件,下圖為截圖):
- 查看內存使用 go tool pprof http://localhost:8089/debug/pprof/heap 終端查看:
可視化(原是 svg 文件,下圖為截圖:
go tool pprof --text http://localhost:6060/debug/pprof/heap
資料
http://www.csdn.net/article/2012-07-04/2807113-less-is-exponentially-more
http://www.jianshu.com/p/91e40c3e3acb?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
大牛真身
最大牌的當屬B和C語言設計者、Unix和Plan 9創始人、1983年圖靈獎獲得者Ken Thompson,這份名單中還包括了Unix核心成員Rob Pike(go語言之父)、java HotSpot虛擬機和js v8引擎的開發者Robert Griesemer、Memcached作者Brad Fitzpatrick,等等。
功能
- 規范的語法(不需要符號表來解析)
- 垃圾回收(獨有)
- 無頭文件
- 明確的依賴
- 無循環依賴
- 常量只能是數字
- int和int32是兩種類型
- 字母大小寫設置可見性(letter case sets visibility)
- 任何類型(type)都有方法(不是類型)
- 沒有子類型繼承(不是子類)
- 包級別初始化以及明確的初始化順序
- 文件被編譯到一個包里
- 包package-level globals presented in any order
- 沒有數值類型轉換(常量起輔助作用)
- 接口隱式實現(沒有“implement”聲明)
- 嵌入(不會提升到超類)
- 方法按照函數聲明(沒有特別的位置要求)
- 方法即函數
- 接口只有方法(沒有數據)
- 方法通過名字匹配(而非類型)
- 沒有構造函數和析構函數
- postincrement(如++i)是狀態,不是表達式
- 沒有preincrement(i++)和predecrement
- 賦值不是表達式
- 明確賦值和函數調用中的計算順序(沒有“sequence point”)
- 沒有指針運算
- 內存一直以零值初始化
- 局部變量取值合法
- 方法中沒有“this”
- 分段的堆棧
- 沒有靜態和其它類型的注釋
- 沒有模板
- 內建string、slice和map
- 數組邊界檢查