goLang goroutine 和 channel 的理解

原创 学习 GO
阅读数: 963 2019年06月17日

无缓冲与有缓冲:
    c1:=make(chan int)         无缓冲
    c2:=make(chan int,1)      有缓冲,可存放1个值

阻塞:不管事有缓存还是无缓存,当 channel 里面放满了数据(缓存不够了,无法继续写入),就会发生阻塞。
等待:主程序下,写入 channel (写满)的时候,会等待取走数据;读取 channel(无数据)等待写入数据。
主程序与线程:如果没有 channel 的牵绊,主程序不会等待线程。

1、如果在 main 主程序下发生阻塞,就会一直等待这个 channel 被消耗(因为 写和读都完成后,才算是一个完整的 channel 动作,带有原子性)
就会报错:

package main

import (
    "fmt"
)

var c chan int

func send(i int) {
    fmt.Println("send -> ", i)
    c <- i
}

func receive() {
    fmt.Println("receive : ", <-c)
}

func main() {
    count := 10
    c = make(chan int)

    for i := 0; i < count; i++ {
        send(i)
    }

    for i := 0; i < count; i++ {
        receive()
    }

}

#go run test.go

send ->  0
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.send(0x0)
    D:/Golang/Test/src/goroutine.go:11 +0xc9
main.main()
    D:/Golang/Test/src/goroutine.go:23 +0x69
exit status 2

#报错原因:因为写入发送了阻塞,后面的 receive() 都没机会执行。
#解决方法:使用 go send(i) 或者 设置 channel 缓存数大于等于写入数量,这时,写入的时候不会发生阻塞。


2、如果在 goroutine(线程)发生阻塞,不会占用主程序进程,也不会等待 channel 被消耗,就不会报错。
因为主程序执行完就关闭进程了,线程就没了。
当然主程序可以读取 channel,把 channel 读取出来,这个时候主程序会等待把 channel 都读完才结束程序。

个人感觉使用 goroutine 去写 channel,通过主线程读 channel 才是正确的使用姿势。


#使用 goroutine

package main

import (
    "fmt"
)

var c chan int

func send(i int) {
    fmt.Println("send -> ", i)
    c <- i
}

func receive() {
    fmt.Println("receive : ", <-c)
}

func main() {
    count := 10
    c = make(chan int)

    for i := 0; i < count; i++ {
        go send(i)
    }

    for i := 0; i < count; i++ {
        receive()
    }

}

//------输出------
E:/Go/bin/go.exe run goroutine.go [D:/Golang/Test/src]
send ->  0
receive :  0
send ->  9
receive :  9
send ->  1
receive :  1
send ->  7
receive :  7
send ->  8
receive :  8
send ->  2
receive :  2
send ->  5
receive :  5
send ->  6
receive :  6
send ->  4
receive :  4
send ->  3
receive :  3
命令退出代码 0.


#增加缓存数量,不小于写入数

package main

import (
    "fmt"
)

var c chan int

func send(i int) {
    fmt.Println("send -> ", i)
    c <- i
}

func receive() {
    fmt.Println("receive : ", <-c)
}

func main() {
    count := 10
    c = make(chan int, 10)

    for i := 0; i < count; i++ {
        send(i)
    }

    for i := 0; i < count; i++ {
        receive()
    }

}

//------输出-----
E:/Go/bin/go.exe run goroutine.go [D:/Golang/Test/src]
send ->  0
send ->  1
send ->  2
send ->  3
send ->  4
send ->  5
send ->  6
send ->  7
send ->  8
send ->  9
receive :  0
receive :  1
receive :  2
receive :  3
receive :  4
receive :  5
receive :  6
receive :  7
receive :  8
receive :  9
命令退出代码 0.


#主程序并未等待线程完成,就退出了,线程只执行了2次就没了。

package main

import (
    "fmt"
)

var c chan int

func send(i int) {
    fmt.Println("send -> ", i)
    c <- i
}

func receive() {
    fmt.Println("receive : ", <-c)
}

func main() {
    count := 10
    c = make(chan int, 10)

    for i := 0; i < count; i++ {
        send(i)
    }

    for i := 0; i < count; i++ {
        go receive()
    }

}

//------输出----
E:/Go/bin/go.exe run goroutine.go [D:/Golang/Test/src]
send ->  0
send ->  1
send ->  2
send ->  3
send ->  4
send ->  5
send ->  6
send ->  7
send ->  8
send ->  9
receive :  0
receive :  1
命令退出代码 0.


phpriji.cn | 网站地图 | 沪ICP备17015433号-1