不同 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

 

Go channel与锁
标签: