【青训营笔记】go语言进阶

36 阅读3分钟

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

Goroutine

go语言实现了更加轻量的协程。

  • 线程 MB级别
  • 协程 KB级别 image.png
package main  
  
import (  
        "fmt"  
        "time"  
)  
  
func say(s string) {  
        for i := 0; i < 5; i++ {  
                time.Sleep(100 * time.Millisecond)  
                fmt.Println(s)  
        }  
}  
  
func main() {  
        go say("world")  
        say("hello")  
}

CSP(Communicating Sequential Processes)

用于描述两个并发的实体通过共享的通讯管道(channel)进行通信的 并发模型

image.png

channel

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

image.png

package main  
  
import "fmt"  
  
func sum(s []int, c chan int) {  
        sum := 0  
        for _, v := range s {  
                sum += v  
        }  
        c <- sum // 把 sum 发送到通道 c  
}  
  
func main() {  
        s := []int{728, -940}  
  
        c := make(chan int)  
        go sum(s[:len(s)/2], c)  
        go sum(s[len(s)/2:], c)  
        x, y := <-c, <-c // 从通道 c 中接收  
  
        fmt.Println(x, y, x+y)  
}

并发安全

go 中有两种锁,为 sync.Mutexsync.RWMutex

  • sync.Mutex 的锁只有一种锁:Lock(),它是绝对锁,同一时间只能有一个锁。

  • sync.RWMutex 叫读写锁,它有两种锁: RLock()Lock()

    • RLock() 叫读锁。它不是绝对锁,可以有多个读者同时获取此锁(调用 mu.RLock)。

    • Lock() 叫写锁,它是个绝对锁,就是说,如果一旦某人拿到了这个锁,别人就不能再获取此锁了。

package main

import (
    "fmt"
    "sync"
    "time"
)
var (
	x int64
	l sync.RWMutex
)
func addWithLock(){
	for i:=0;i<2000;i++{
		l.Lock()
		x+=1
		l.Unlock()
	}
}
func addWithoutLock(){
	for i:=0;i<2000;i++{
		x+=1
	}
}
func main(){
   x =0
   for i:=0;i<5;i++{
	go addWithLock()
   }
   time.Sleep(time.Second)
   fmt.Println("withLock",x)
   x=0
   for i:=0;i<5;i++{
	go addWithoutLock()
   }
   time.Sleep(time.Second)
   fmt.Println("withoutLock",x)

}
	

语言等待组

Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务

在 sync.WaitGroup(等待组)类型中,每个 sync.WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。

等待组有下面几个方法可用,如下表所示。

Add(delta int) 等待组的计数器 +1
Done() 等待组的计数器 -1
Wait() 当等待组计数器不等于 0 时阻塞直到变 0。

package main
import (
    "fmt"
    "sync"
)
func main(){
	var wg  sync.WaitGroup
	wg.Add(5)
	for i:=0;i<5;i++{
		go func (j int){
			fmt.Println(j)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

依赖管理

go语言依赖管理的演进

image.png GOPATH弊端:不同项目不能依赖同一个库的不同版本 GO Vendor弊端:不能很清晰的标识依赖的版本概念

go module 是Go语言从 1.11 版本之后官方推出的版本管理工具,并且从 Go1.13 版本开始,go module 成为了Go语言默认的依赖管理工具

  • 通过go.mod文件管理依赖包版本(配置文件,描述依赖)
  • 通过go get/go mod指定工具管理依赖包
  • 通过Proxy对依赖进行分发

依赖管理三要素

image.png 依赖分发 当依赖分发的时候,可能由于第三方的修改删除造成的代码变动给给我们带来不便,而同时我们也会增加第三方的压力。

image.png 我们使用Proxy就可以很好的解决这个问题

image.png 依赖工具

image.png

image.png

测试

image.png 从上到下,覆盖率逐渐变大,成本却逐渐变低