「Go 语言上手 - 工程实践」收获| 青训营笔记

113 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

1.并发和并行的区别

并发是多线程程序在一个核的CPU上运行

image.png

并行是多线程程序在多个核的CPU上运行

image.png 由此可见,GO语言可以充分发挥多核优势,高效运行。

2.Goroutine

线程是系统里比较昂贵的资源,创建,切换,停止都消耗很多资源栈内存在KB级别,相比较而言携程更加轻量级栈内存在MB级别。 例:

func hello(i int) {
   fmt.Println("hello goroutine" + fmt.Sprintln(i))
}
func HelloGoRoutine() {
   for i := 0; i < 5; i++ {
      go func(j int) {
         hello(j)
      }(i)
   }
   time.Sleep(time.Second)
}

结果为

image.png

3.CSP(Communicating Sequential Processes)

提倡通过通信共享内存,而不是通过共享内存实现通信。

image.png 左图的通道就是线程之间传递信息的通道,遵循先入先出,是有序的。 右图是共享内存实现通信,通过互斥量进行加锁,容易发生数据竟态的问题。

4.channel

channel是线程之间通信的通道,声明语法为make(chan 元素类型,【缓冲大小】)如:

make(chan int) //int类型无缓冲通道
make(chan int, 3) //int类型有缓冲通道

无缓冲通道的时候,发送和接收是同步的,也被称为同步通道。有缓冲通道可以实现队列结构,阻塞一定的数据。例:

func test() {
   c1 := make(chan int)
   c2 := make(chan int, 3)
   go func() {
      defer close(c1)
      for i := 0; i < 10; i++ {
         c1 <- i
      }
   }()
   go func() {
      defer close(c2)
      for i := range c1 {
         c2 <- i * i
      }
   }()
   for i := range c2 {
       //复杂操作
      fmt.Println(i)
   }
}

执行结果:

image.png c2使用了有缓冲的通道,是因为考虑到消费者的消费速度比较慢,为防止其影响生产者的生产速度,所以有缓存区。

5.并发安全Lock

GO语言中也有锁,可以保证并发安全。

var (
   x    int64
   lock sync.Mutex
)
func AddWithLock() {
   for i := 0; i < 10; i++ {
      lock.Lock()
      x += 1
      lock.Unlock()
   }
}
func AddWithoutLock() {
   for i := 0; i < 10; i++ {
      x += 1
   }
}
func Add() {
   x = 0
   for i := 0; i < 1000; i++ {
      go AddWithLock()
   }
   time.Sleep(time.Second)
   fmt.Println(x)
   x = 0
   for i := 0; i < 1000; i++ {
      go AddWithoutLock()
   }
   time.Sleep(time.Second)
   fmt.Println(x)
}

运行结果:

image.png

6.WaitGroup

image.png 此类有三个函数,计数器为0时 Wait停止阻塞,Add(n)计数器+n,Done完成后计数器-1。 代码示例:

func HelloGoRoutine() {
   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.Done之前要有defer关键字,意思时,执行完以下函数后才让计数器减一。

不同的模式

1.GOPATH模式 GOPATH模式中,把所有的源码放在src下,项目代码直接依赖src的代码。 弊端:如果项目AB同时依赖某一个包的不同版本,不可实现版本控制。 2.Go Vendor模式 每个项目引入一份依赖的副本,储存依赖的副本,解决了多个项目需要同一个包的依赖冲突的问题。 弊端:包也可以相互依赖,如果A项目依赖包BC,包BC同时依赖包D的不同版本,即导致冲突,编译出错。 3.Go Module模式 通过go.mod文件管理依赖包版本,通过go get/go mod指令工具管理依赖包。