后端go语言实例学习| 青训营笔记

61 阅读3分钟

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

image.png

今天的笔记是来总结、复习前段时间所学习的 Go 语言进阶与依赖管理

GO语言的一大特点就是快。

image.png 高并发的特点就可以使得程序能够高效率运行。

1.1 Goroutine

image.png 协程:用户态,轻量级线程,栈MB级别。 线程:内核态,线程跑多个协程,栈KB级别。

package main

import (
	"fmt"
	"time"
)

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

打印结果:

image.png

1.2 CSP

image.png GO提倡通过通信来共享内存,而不是通过共享内存而实现通信。

1.3channel

image.png 无缓冲通道被称为同步通道。 有缓冲通道类似生产消费模型,有阻塞的发生。

  • 如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

  • 上述代码中之所以能够顺利从通道接收到数据,是因为每次遍历之前都通过关闭对应的通道后再进行的遍历接受数据

image.png exp:

package main

func CalSquare() {
   src := make(chan int)
   dest := make(chan int, 3)
   go func() {
      defer close(src)
      for i := 0; i < 10; i++ {
         src <- i
      }
   }()
   go func() {
      defer close(dest)
      for i := range src {
         dest <- i * i
      }
   }()
   for i := range dest {
      println(i)
   }

}

打印结果:

image.png

1.4 并发安全LOCK

image.png 若采用共享内存实现通信,则会出现多个Goroutine同时操作一块内存资源的情况,这种情况会发生竞态问题(数据竞态)

  • 互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。
package concurrence
​
import (
    "sync"
    "time"
)
​
var (
    x    int64
    lock sync.Mutex
)
​
func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    }
}
func addWithoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}
​
func Add() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    time.Sleep(time.Second)
    println("WithoutLock:", x)
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock:", x)
}
​
func ManyGoWait() {
    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()
}
  • 使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;

  • 当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。

WaitGroup解决数据竞争

Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务 WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。

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