这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
GO基础(三)
接口
package Test
import "testing"
// 定义一个接口
type Programmer interface {
WriteHelloWorld() string
}
type GoProgrammer struct {
}
func (g *GoProgrammer) WriteHelloWorld() string {
return "fmt.println("Hello World")"
}
func TestClient(t *testing.T) {
var p Programmer
p = new(GoProgrammer)
t.Log(p.WriteHelloWorld())
}
接口差异
1.接口为非侵入性,实现不依赖于接口定义
2.所以接口的定义可以包含在接口使用者包内
空接口和断言
1.空接口可以表示任何类型
2.通过断言来将空接口转换为制定类型
v,ok :=p.(int) //ok=true 时转换成功
Go接口最佳选择
1.倾向于使用小的接口定义,很多接口只包含一个方法
2.较大的接口定义,可以由多个小接口定义组合而成
3.只依赖于必要功能的小接口
错误机制
差异
- 没有异常机制
- error 类型实现了error接口
- 可以通过errors.New来快速创建错误实例
- 尽早失败,防止嵌套
可复用模块
package包
- 基本复用模块单元,以首字母大写来表明可以被包外代码访问
- 代码的package可以和所在的目录不一致
- 同一个目录里的Go代码的package要一致
init方法
- 在main被执行前,所有依赖的package的init方法都会被执行
- 不同包的init函数按照包导入的依赖关系决定执行顺序
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数
并发机制
协程机制
package Test
import (
"fmt"
"testing"
)
func TestGroutine(t *testing.T) {
for i := 0; i < 10; i++ {
go func(i int) { //在方法名前加go为协程
fmt.Println(i)
}(i)
}
}
共享内存
func TestShare(t *testing.T) {
counter := 0
//创建五千个线程
for i := 0; i < 5000; i++ {
go func() {
counter++
}()
}
time.Sleep(1 * time.Second)
t.Log(counter)
}
//线程不安全,导致coubter!=5000
锁
package Test
import (
"sync"
"testing"
"time"
)
func TestShare(t *testing.T) {
var mut sync.Mutex
counter := 0
//创建五千个线程
for i := 0; i < 5000; i++ {
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
}()
}
time.Sleep(1 * time.Second)
t.Log(counter)
}
等待
上面代码使用time.Sleep方法等待所有协程执行完毕,实际上应该使用wait方法等待线程执行完毕
package Test
import (
"sync"
"testing"
)
func TestShare(t *testing.T) {
var mut sync.Mutex
var wg sync.WaitGroup
counter := 0
//创建五千个线程
for i := 0; i < 5000; i++ {
wg.Add(1)
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
wg.Done()
}()
}
wg.Wait()
t.Log(counter)
}