GO协程| 青训营笔记

31 阅读3分钟

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

GO协程

GO我最先听到就是因为GO的协程在多并发场景下的处理能力,文中就来看看GO协程的相关概念和原理。

进程、线程、协程

进程:一个运行的代码就是进程,进程分配的资源是计算机分配的控制计算机资源的最大资源。

线程:是程序执行的最小单元,多个进程之间共享了堆,独占栈,PC,等资源。

协程:是一种轻量级的函数。一个线程可以有多个协程。由程序控制,协程交换不用上下文切换。

并发与并行概念

并行:同一时刻有多个执行流,同一段时间有多个执行流

并发:同一时刻只有1个执行流,通过快速切换,同一时间段有多个执行流。

Goroutine

线程属于内核态,比较消耗资源

协程轻量级,用户态,通过go关键字进行调用,线程之间有很多通信方式,协程之间可以通过channel和共享内存进行通信。

使用

使用go关键字,后面跟上函数就可以。

通信机制

  • 使用通道的方式进行通信,先进先出的原理,管道中的数据有序的,并发的时候利用这个机制那么协程之间的数据就比较好维护。所以我们选择的都是channel来进行协程之间的通信,很少采用共享内存方式进行通信。
  • 使用共享内存的方式进行通信,里面就需要上锁来维护并发一致性。

channel通信,更加快

通过共享内存,这里会互斥锁

通道channel

推荐使用通道进行协程通信。channel像队列一样先进先出,获取数据的函数没有数据会进行阻塞,通过make(chan 元素类型,[缓冲大小])的方式创建一个通道,然后通过chan<-方式像channel中传入数据,

项目演示:

打印1-9数的平方

协程A生产数字

协程B消费数字

使用defer关键字延迟一个函数或者方法的执行。defer语句会在所有函数最后去执行。

package main

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

func main() {
    CalSquare()

}

使用dest,用了缓冲,消费者速度可能比生产者慢,

package main

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 addWithLock()
	}
	time.Sleep(time.Second)
	println(x)
	x = 0
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	println(x)
}

func main() {
	Add()
}

waitGroup

底层通过计数器来标识任务数,调用Wait()进行阻塞,完成一个任务调用Done()方法,计数器减1,到0就不会阻塞。

协程和线程之间的演示项目:

func hello(i int) {
	println("hello goroutine :" + fmt.Sprint(i))
}
func helloGoWait() {
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func(j int) {
			//推迟执行完函数才进行done
			defer wg.Done()
			hello(j)
		}(i)
	}
	wg.Wait()
}

总结:

协程的使用没有太难,但是还是有很多不理解的地方,主要在原理部分。