go协程入门| 青训营

68 阅读2分钟

并行和并发

参考

项目地址 go-project-example

image-20230726205301249

go就是基于并发比较diao的玩意,为了适应多核以达到并行

协程(Goroutine)

image-20230726205411246

例子

  • 快速打印例子

    hello world : 4 hello world : 3 hello world : 2 hello world : 1 hello world : 0

    for i := 0; i < 5; i++ {
        go func(j int) { //i:协程共有,j:协程独有
            println("hello world : " + fmt.Sprint(j))
        }(i)
    }
    time.Sleep(time.Second) //1秒

小优化

  • Sleep() 等待线程结束不是好东西

  • 使用协程同步的WaitGroup,这个玩意提供了三个方法,类似于wait() 等待协程,进行同步

    本质就是计数器:

    • 开启协程+1;
    • 执行结束-1;
    • 主协程阻塞直到计数器为0。

image-20230726211234650

    var wg sync.WaitGroup//声明
    for i := 0; i < 5; i++ {
        wg.Add(1)//+1
        go func(j int) {
            defer wg.Done()//-1
            println("hello world : " + fmt.Sprint(j))
        }(i)
    }
    wg.Wait()//阻塞主线程

协程同步

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

image-20230726205930070

通道通信 (Channel)

参考

ch :=make(chan 元素类型,[缓冲大小])//定义
ch <- i     // 向ch发送数据
v := <-ch   // 从Channel ch中接收数据,并将数据赋值给v; (箭头的指向就是数据的流向)
defer close(ch) //会返回一个空的构造体
  • 无缓冲通道 : make(chan int)

    • 一个资源量,直接堵塞

image-20230726210216032

  • 有缓冲通道 : make(chan int,2)

    • 就是生产者消费者模式,使用资源量进行一个协程同步的

image-20230726210204121

func main() {
    CalSquare()
    time.Sleep(1 * time.Second)
}
​
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 //将生产者的数据发送到dest通道
        }
    }()
    for i := range dest { //兼容打印速度,生成数据的速度快
        println(i)
    }
}

通道关闭事项:生产者多将通道关闭了,消费者还是可以取数据

  • 关闭通道是一种通知接收者不再有新数据发送的方式。
  • 已关闭的通道仍然可以被读取,接收者可以继续读取通道中已经发送的数据,直到通道为空。
  • 在从已关闭的通道接收完所有数据后,后续的接收操作会返回通道元素类型的零值,且不会阻塞。
  • 尝试在已关闭的通道上发送数据将导致 panic,且不要关两次

两个通道

  • 生产者的通道src速度快
  • 消费者将数据取出发送到dest通道
  • dest通道 兼容打印速度,使用缓存队列

资源 (Sync)

对于共享的数据,使用lock加锁,只限于一个协程访问

var (
    x    int64
    lock sync.Mutex             //1、
)
​
func addWithLock() {//加锁
    for i := 0; i < 2000; i++ {
        lock.Lock()             //2
        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 addWithLock()
    }
    time.Sleep(time.Second)
    fmt.Println(x) //10000
​
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    time.Sleep(time.Second)
    fmt.Println(x)  //6531
}