Go 语言进阶 - 工程进阶 | 青训营笔记

62 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天。

语言进阶与依赖管理

1.协程 Goroutine
例:go func(j int){ fmt.Println(j) }(i)

2.通道 Channel
无缓冲通道 make(chan int)
有缓冲通道 make(chan int,2)
有缓冲通道发送数据的时候只有在缓冲区满的时候才会阻塞。同样的,接收数据的时候只有在缓冲区为空的时候才阻塞。
通过通信实现共享内存:
例:

src := make(chan int)  //创建一个通道
src <- i  //把i发送到channel src中
j := <- src  //从src中接收数据赋给j
for i := range src  // 遍历src中的数据


3.并发安全 Lock
当多个协程对同一个变量进行更改时,可能会出现并发安全问题。

var {
    x int
    lock sync.Hutex
}
lock.Lock()
x++   // 这里插入读写操作
lock.Unlock()


4.WaitGroup
用来实现并发任务的同步。 声明:var wg sync.WaitGroup
首先设置协程的数量 例:wg.Add(5)
在每次执行完一个协程后计数器减一

for i := 0; i < 5; i++{
    go func(j int){
        defer wg.Done()
        hello(j)
    }(i)
}
wg.Wait()
// 最后加上wg.Wait(),进行阻塞,保证子协程执行完之前主协程不退出。

5.go.mod
使用go mod init ProjectName生成go.mod文件,文件中第一行显示依赖管理的基本单元,为module + 自己init时写的ProjectName。接着是go原生库版本,最后是单元依赖。
6.Proxy 依赖分发
保证依赖分发的稳定性
7.go get/mod:

image.png

image.png

8.单元测试:

  • 测试文件以_test.go结尾
  • 方法名规则为TestXxx(*testint.T)
  • 初始化逻辑放在TestMain中 单元测试例:
func HelloTom() string {
    return "Tom"
}
func TestHelloTom(t *testing.T){
    output := HelloTom()
    expectOutput := "Tom"
    if expectOutput != output {
        t.errorf("Expected %s do not match actual %s",expectOutput,output)
    }
}
monkey.Patch(target,replacement interface{}) //将target函数替换成replacement
monkey.Unpatch(target) //取消打桩

实战:
根据初始化话题数据索引写出初始化回帖数据索引:

func initPostIndexMap(filepath string) (error,map[int64]*Post) {
	open,err := os.Open(filepath+"post")
	if err != nil {
		return err,nil
	}
	scanner := bufio.NewScanner(open)
	PostsMap := make(map[int64]*Post)
	for scanner.Scan() {
		text := scanner.Text()
		var post Post
		if err := json.Unmarshal([]byte(text),&post); err != nil{
			return err,nil
		}
		PostsMap[post.id] = &post
	}
	return nil,PostsMap
}

课后实践中存在的问题:
1.生成帖子时随机的id可能会和已有帖子的id重复
解决:在for循环例每生成一个随机id就查询PostsMap[id]是否存在,若不存在,则该id可用,退出循环。
2.Map并发性问题
解决:使用Lock或者使用sync.Map
sync.Map使用:

  var scene sync.Map

 // 将键值对保存到sync.Map
  scene.Store("greece", 97)
  scene.Store("london", 100)
  scene.Store("egypt", 200)

  // 从sync.Map中根据键取值
  fmt.Println(scene.Load("london"))

  // 根据键删除对应的键值对
  scene.Delete("london")

  // 遍历所有sync.Map中的键值对
  scene.Range(func(k, v interface{}) bool {
    fmt.Println("iterate:", k, v)
    return true
  })