一、接口 例子 定义测试接口 testInterface/mock/main.go
1 2 3 4 5 6 7 8 9 10 package mocktype Retriever struct { Contents string } func (r Retriever) Get(url string ) string { return r.Contents }
定义真实接口 testInterface/real/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package real import ( "net/http" "net/http/httputil" "time" ) type Retriever struct { UserAgent string TimeOut time.Duration } func (r Retriever) Get(url string ) string { res,err := http.Get(url) if err != nil { panic (err) } result,err := httputil.DumpResponse(res,true ) if err != nil { panic (err) } res.Body.Close() return string (result) }
使用两个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "fmt" "testInterface/mock" "testInterface/real" ) type Retriver interface { Get(url string ) string } func download (r Retriver) string { return r.Get("https://baidu.com" ) } func main () { var r Retriver r = mock.Retriever{Contents: "this is a mock" } fmt.Println(download(r)) var newR Retriver newR = real .Retriever{} fmt.Println(download(newR)) }
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "fmt" ) type Phone interface { call() } type NokiaPhone struct {} func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!" ) } type IPhone struct {} func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!" ) } func main () { var phone Phone phone = new (NokiaPhone) phone.call() phone = new (IPhone) phone.call() }
二、函数式编程 闭包实现一个累加器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func adder () func (int ) int { sum := 0 return func (v int ) int { sum += v return sum } } func main () { a := adder() for i := 0 ; i < 10 ; i ++{ fmt.Printf("0 加到 %d 是 %d\n" ,i,a(i)) } }
正统函数式编程,实现上述例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type iAdder func (int ) (int , iAdder)func adder (base int ) iAdder { return func (v int ) (int ,iAdder) { return base + v, adder(base + v) } } func main () { a := adder(0 ) for i := 0 ; i < 10 ; i ++{ var s int s,a = a(i) fmt.Printf("0 加到 %d 是 %d\n" ,i,s) } }
利用闭包实现,斐波那契数列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func fibonacci () func () int { a,b := 0 ,1 return func () int { a,b = b, a + b return a } } func main () { f := fibonacci() fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) }
函数实现接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 type intGen func () int func (g intGen) Read (p []byte ) (n int , err error ) { next := g() if next > 10000 { return 0 ,io.EOF } s := fmt.Sprintf("%d\n" ,next) return strings.NewReader(s).Read(p) } func printFileContent (reader io.Reader) { scanner := bufio.NewScanner(reader) for scanner.Scan() { fmt.Println(scanner.Text()) } } func fibonacci () intGen { a,b := 0 ,1 return func () int { a,b = b, a + b return a } } func main () { f := fibonacci() printFileContent(f) }
三、错误处理和资源管理 defer 调用 例子 1 2 3 4 5 6 7 8 9 func main () { fmt.Println(1 ) defer fmt.Println(3 ) fmt.Println(2 ) defer fmt.Println(4 ) }
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main () { file,err := os.Create("haha.txt" ) if err != nil { panic (err) } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() for i := 0 ; i < 100 ; i ++ { fmt.Fprintln(writer,i) } }
错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main () { file,err := os.OpenFile("haha.txt" ,os.O_EXCL|os.O_CREATE,0666 ) if err != nil { if patherr,ok := err.(*os.PathError);ok{ fmt.Println(patherr.Op) fmt.Println(patherr.Path) fmt.Println(patherr.Err) } else { err = errors.New("other error" ) panic (err) } return } defer file.Close() }
四、测试与性能调优 表格驱动测试 main.go
1 2 3 4 5 6 7 8 package mainfunc Add (a,b int ) int { return a + b } func main () {}
add_test.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "testing" func TestAdd (t *testing.T) { tests := []struct { a,b,c int } { {1 ,2 ,3 }, {1 ,5 ,3 }, {3 ,3 ,6 }, } for _,tt := range tests{ if res := Add(tt.a,tt.b); res != tt.c{ t.Errorf("计算错误:%d + %d = %d,而不是 %d" ,tt.a,tt.b,res,tt.c) } } }
开始测试
代码覆盖率测试 1 2 go test -coverprofile=cover.out ./internal/... go tool cover -html=cover.out
(./internal/…) 表示internal下所有的测试代码
代码性能测试(基准测试) 1 2 3 4 5 6 7 8 9 10 11 func BenchmarkAdd (b *testing.B) { var a,aa int a = 1000 aa = 2000 for i := 0 ; i < b.N; i++ { if res := Add(a,aa); res != 3000 { b.Errorf("计算错误:%d + %d = %d,而不是 %d" ,a,aa,res,3000 ) } } }
测试命令
利用 pprof 进行性能调优 查看性能 1 2 3 go test -bench . -cpuprofile cpu.out go tool pprof cpu.out web
五、Goroutine(协程) goroutine 概述
轻量级“线程”
非抢占式多任务处理,由协程主动交出控制权
编译器 / 解释器 / 虚拟机层面的多任务
多个协程可能在一个或多个线程上运行(线程数量一般不大于机器核数)
goroutine 并发例子 1 2 3 4 5 6 7 8 9 10 func main () { for i := 0 ; i < 1000 ; i++ { go func (j int ) { fmt.Println("i am routine" ,j) }(i) } time.Sleep(time.Millisecond) }
goroutine 可能会切换的点
解释:任何函数加上go就能送到调度器运行,调度器会在合适的位置进行协程的切换
I/O,select
channel
等待锁
函数调用(有时)
runtime.Gosched()
检测数据访问冲突(检测race condition) 代码
1 2 3 4 5 6 7 8 9 10 11 func main () { a := 1 for i := 0 ; i < 1000 ; i++ { go func (j int ) { for { a++ } }(i) } time.Sleep(time.Millisecond) }
检测命令
六、Channel(通道) 简单收发channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func main () { chanDemo() } func chanDemo () { c := make (chan int ) go func () { for { n := <- c fmt.Println(n) } }() c <- 1 c <- 2 time.Sleep(time.Millisecond) }
channel 批量收发数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func main () { chanDemo() } func chanDemo () { var channels [10 ]chan int for i := 0 ; i < 10 ; i++ { channels[i] = make (chan int ) go worker(i,channels[i]) } for i := 0 ; i < 10 ; i++ { channels[i] <- i + 'a' } time.Sleep(time.Millisecond) } func worker (i int , c chan int ) { for { n := <- c fmt.Printf("接收来自 %d 通道,数据%v\n" ,i,n) } }
channel 通道类型 1 2 3 4 5 6 7 8 var a chan int var b chan <- int var c <-chan int
channel 的缓冲区 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func main () { bufferedChan() } func bufferedChan () { c := make (chan int ,3 ) go worker(0 ,c) c <- 11 c <- 22 c <- 33 close (c) time.Sleep(time.Millisecond) } func worker (i int , c chan int ) { for n := range c { fmt.Printf("接收来自 %d 通道,数据%v\n" ,i,n) } }
channel 等待所有 goroutine 结束 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 func main () { chanDemo() } type workStruct struct { in chan int done chan bool } func chanDemo () { var channels [10 ]workStruct for i := 0 ; i < 10 ; i++ { channels[i] = workStruct{ in : make (chan int ), done: make (chan bool ), } } for i,w := range channels{ go worker(i,w) } for i,w := range channels{ w.in <- 'a' + i } for _,w := range channels{ <-w.done close (w.in) close (w.done) } fmt.Printf("执行后续操作" ) } func worker (i int , c workStruct) { for n := range c.in { fmt.Printf("接收来自 %d 通道,数据%v\n" ,i,n) c.done <- true } }
WaitGroup 等待所有 goroutine 结束 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 func main () { chanDemo() } type workStruct struct { in chan int wg *sync.WaitGroup } func chanDemo () { var wg sync.WaitGroup var channels [10 ]workStruct for i := 0 ; i < 10 ; i++ { channels[i] = workStruct{ in : make (chan int ), wg: &wg, } } wg.Add(10 ) for i,w := range channels{ go worker(i,w) } for i,w := range channels{ w.in <- 'a' + i } wg.Wait() fmt.Printf("执行后续操作" ) } func worker (i int , c workStruct) { for n := range c.in { fmt.Printf("接收来自 %d 通道,数据%v\n" ,i,n) c.wg.Done() } }
select 接收或发送某个 channel 的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func main () { var c1, c2 = generator(), generator() for { select { case n := <-c1: fmt.Println("c1里面来了数据" , n) case n := <-c1: fmt.Println("走这里" , n) case n := <-c2: fmt.Println("c2里面来了数据" , n) } } } func generator () chan int { out := make (chan int ) go func () { i := 0 for { time.Sleep(time.Duration(rand.Intn(100 )) * time.Millisecond) out <- i i++ } }() return out }
select 语法概述
每个 case 都必须是一个通信
所有 channel 表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通信可以进行,它就执行,其他被忽略。
如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。
否则:
如果有 default 子句,则执行该语句。
如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值
传统同步机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 type atomicInt struct { value int lock sync.Mutex } func (a *atomicInt) increment() { a.lock.Lock() defer a.lock.Unlock() a.value++ } func (a *atomicInt) get() int { a.lock.Lock() defer a.lock.Unlock() return int (a.value) } func main () { var a atomicInt a.increment() go func () { a.increment() }() time.Sleep(time.Millisecond) fmt.Println(a.get()) }