channel用法和源码

源码分析https://mp.weixin.qq.com/s/eCwZMwGjU2yoXu6K2nGF3g

 

1 当chan用close关闭时,无论是有缓存的还是无缓存的,返回已缓冲数据或零值,如果重复用close关闭chan或对关闭的chan写入值都会报错,

https://blog.csdn.net/qq_41065919/article/details/107729571

https://www.cnblogs.com/pluse/p/12167537.html

struct{}类型好像只有一种具体实例即struct{}{},chan中如果有这种类型,只能写入它的零值struct{}{},所以类型为struct{}的chan无论是写入还是读出都时零值,

func main()  {
    m := make(chan struct{}, 1)
    // 从空的m中一直读取值会一直阻塞,所以会报死锁的错误,all goroutines are asleep - deadlock!
    //b := <- m
    //fmt.Println(b)
    // 如果这里就关闭了协程,下面会直接输出chan中的零值,而不会走default,
    //close(m)
    //m <- struct{}{}
    go func() {
        for{
            select {
            case a := <- m:
                fmt.Println(a)
                time.Sleep(time.Second)
            default:
                fmt.Println("chan为空,读取的时候会阻塞")
                time.Sleep(time.Second)
            }
        }
    }()
    // 加这个是为了主协程等待里面的子协程,否则的话主协程直接执行完了,观察不到子协程的具体输出,
    time.Sleep(time.Second * 5)
}
View Code

a 如果close无缓存channel,

1> close前没有因为写入阻塞,则读出的是channe里类型的零值,

2> 如果写入阻塞了,则第一次读出的是写入的值,之后读出的是零值,

func main()  {
    m := make(chan int)
    go func() {
        m <- 555
        // 这里虽然close了,但之后第一次读出的仍是第一次写入的值,
        close(m)
        // 保证close在读之前
        time.Sleep(time.Second * 3)
    }()
    go func() {
        for{
            select {
            case a := <- m:
                if a != 0{
                    fmt.Println("第一次从channel中读出的值为colse前写入的值", a)
                } else{
                    fmt.Println("之后读出的值为int类型的零值", a)
                }
                time.Sleep(time.Second)
            default:
                fmt.Println("chan为空,读取的时候会阻塞")
                time.Sleep(time.Second)
            }
        }
    }()
    time.Sleep(time.Second * 4)
}
View Code

b 如果close有缓存channel,

1> close前没有写入,则读出的是channel里类型的零值,

2> 如果close前写入了,则会按队列的顺序把数据依次取出来,

func main()  {
    m := make(chan int, 3)
    m <- 111
    m <- 222
    close(m)
    go func() {
        for{
            select {
            case a := <- m:
                if a != 0{
                    fmt.Println("读出的值是之前写入的值", a)
                } else{
                    fmt.Println("之后读出的值为int类型的零值", a)
                }
                time.Sleep(time.Second)
            default:
                fmt.Println("chan为空,读取的时候会阻塞")
                time.Sleep(time.Second)
            }
        }
    }()
    time.Sleep(time.Second * 4)
}
View Code

待学习:https://i6448038.github.io/2019/04/11/go-chan

反作弊解封代码

func main(){
    goroutine_count := 2
    ch := make(chan int, goroutine_count)
    var wg sync.WaitGroup
    wg.Add(1024)
    for i:=0; i<1024; i++{
        ch <- i
        go func(i int) {
            dosomething()
            // 必须要写这个,
            <- ch
            wg.Done()
        }(i)
    }
    wg.Wait()
}
func dosomething()  {
}
View Code

 

posted on 2020-12-31 22:56  吃我一枪  阅读(11718)  评论(0编辑  收藏  举报

导航