go-channel与锁

Golang
395
0
0
2022-11-09
标签   Golang进阶
不同 goroutine 之间如何通讯?有两种方案:
1.全局变量的互斥锁
2.使用管道 channel 来解决
因为没有对全局变量 m 加锁,因此会出现资源争夺问题,代码会出现错误,提示 concurrent map writes
var (
    myMap = make(map[int]int, 10)
    lock sync.Mutex
)

func test(n int) {
    res := 1 
    for i := 1; i <= n; i++ {
        res *= i
    }
    //这里我们将 res 放入到 myMap
    lock.Lock()
    myMap[n] = res //concurrent map writes?
    lock.Unlock()
}
func main()  {
    // 我们这里开启多个协程完成这个任务[200 个] 
    for i := 1; i <= 200; i++ {
        go test(i)
    }
    //休眠 10 秒钟【第二个问题 】
    time.Sleep(time.Second * 10)
    lock.Lock()
    for i, v := range myMap {
        fmt.Printf("map[%d]=%d\n", i, v)
    }
    lock.Unlock()
}
1) 前面使用全局变量加锁同步来解决 goroutine 的通讯,但不完美
2) 主线程在等待所有 goroutine 全部完成的时间很难确定,我们这里设置 10 秒,仅仅是估算。如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有 goroutine 处于工作
状态,这时也会随主线程的退出而销毁
3) 通过全局变量加锁同步来实现通讯,也并不利于多个协程对全局变量的读写操作。
要解决上面的不足,引入一个新的通讯机制-channel
1)channel是一个队列,那么就有先进先出的特点
2)线程安全,多goroutine访问时,不需要加锁,说明channel本身就是线程安全的
3)channel有类型的,一个int的channel只能存放string数据类型
var 变量名 chan 数据类型
var intChan chan
var mapChan chan map[int]string (mapChan 用于存map[int]string 类型)
channel 是引用类型
channel 必须初始化才能写入数据, 即 make 后才能使用
管道是有类型的,intChan 只能写入 整数 int
func main() {
    //演示一下管道的使用 
    //1. 创建一个可以存放 3 个 int 类型的管道 
    var intChan chan int
    intChan = make(chan int, 3)
    //2. 看看 intChan 是什么
    fmt.Printf("intChan 的值=%v intChan 本身的地址=%p\n", intChan, &intChan)
    //3. 向管道写入数据
    intChan<- 10
    num := 211
    intChan<- num
    intChan<- 50 
    // intChan<- 98//注意点, 当我们给管写入数据时,不能超过其容量 
    //4. 看看管道的长度和 cap(容量)
    fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 3, 3 
    //5. 从管道中读取数据 
    var num2 int
    num2 = <-intChan
    fmt.Println("num2=", num2)
    fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan))
    // 2, 3 
    //6. 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
    num3 := <-intChan
    num4 := <-intChan
    num5 := <-intChan
    fmt.Println("num3=", num3, "num4=", num4, "num5=", num5)
}

运行结果:

intChan 的值=0xc0000ba000 intChan 本身的地址=0xc0000b4018
channel len= 3 cap=3 
num2= 10
channel len= 2 cap=3 
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
        /home/malina/gotest/src/test3/main.go:29 +0x395
Process finished with the exit code 2
1) channel 中只能存放指定的数据类型
2) channle 的数据放满后,就不能再放入了
3) 如果从 channel 取出数据后,可以继续放入
4) 在没有使用协程的情况下,如果 channel 数据取完了,再取,就会报 dead lock