首页 > 编程开发 > Go语言    日期:2026-07-08 / 浏览

channel的定义和使用

channel 用于多个 goroutine 之间的通信

package main

import "fmt"

func main() {
    // 创建一个 channel
    c := make(chan int)

    go func() {
        defer fmt.Println("goroutine结束!")

        fmt.Println("goroutine正在运行")
        // 将数据发送到 channel 中
        c <- 100
    }()

    // 从 channel 中接收数据, 存入到 num
    num := <- c

    fmt.Println("num:", num)
    fmt.Println("main goroutine 结束")
}

为什么 main goroutine 中的执行会在 子goroutine 之后?

(1)main go 已经到达 num := <- c 时,如果 channel 中还没有存在 c,就会对 main go 进行阻塞。
(2)sub go 已经到达 c <- 100,如果 main go 还没有到达 num := <- c,因为此时的管道 c 是无缓冲的,就会对 sub go 进行阻塞。

channel——有缓冲和无缓冲同步

1. 无缓冲的channel

(1)在第1步,两个goroutine都到达通道,但哪个都没有开始执行发送或者接收。
(2)在第2步,左侧的goroutine将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个goroutine会在通道中被锁住,直到交换完成。
(3)在第3步,右侧的goroutine将它的手放入通道,这模拟了从通道里接收数据。这个goroutine一样也会在通道中被锁住,直到交换完成。
(4)在第4步和第5步,进行交换,并最终,在第6步,两个goroutine都将它们的手从通道里拿出来,这模拟了被锁住的goroutine得到释放。两个goroutine现在都可以去做其他事情了。

2. 有缓冲的channel

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个容量为3的 channel(有缓冲的channel)
    c := make(chan int, 3)

    go func() {
        defer fmt.Println("子go程结束!")

        for i := 0; i < 3; i++ {
            c <- i
            fmt.Println("子go程 i = ", i, "  元素个数:", len(c), "  元素个数:", cap(c))
        }   
    }()

    time.Sleep(1 * time.Second)

    for i := 0; i < 3; i++ {
        // 从 channel 中获取数据
        num := <- c
        fmt.Println("num =", num)
    }

    fmt.Println("main goroutine 运行结束")

}

(1)在第1步,右侧的goroutine正在从通道接收一个值。
(2)在第2步,右侧的这个goroutine独立完成了接收值的动作,而左侧的goroutine正在发送一个新值到通道里。
(3)在第3步,左侧的goroutine还在向通道发送新值,而右侧的goroutine正在从通道接收另外一个值。这个步骤里的两个操作既不是同步的,也不会互相阻塞。
(4)最后,在第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。

channel——关闭channel

package main

import "fmt"

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

    go func() {
        for i := 0; i < 3; i++ {
            c <- i
        }

        // close 关闭 channel
        close(c)
    }()

    for {
        // ok 为 true 表示 channel 没有关闭, ok 为 false 表示 channel 已经关闭
        if data, ok := <- c; ok {
            fmt.Println("data: ", data) 
        } else {
            break
        }
    }

    fmt.Println("main goroutine 结束")

}

(1)channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者想显式的结束range循环之类的,才去关闭channel;
(2)关闭channel后,无法向channel再发送数据(引发panic错误后导致接收立即返回零值);
(3)关闭channel后,可以继续从channel接收数据;
(4)对于nil channel,无论收发都会被阻塞。

channel 与 range

package main

import "fmt"

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

    go func() {
        for i := 0; i < 3; i++ {
            c <- i
        }

        // close 关闭 channel
        close(c)
    }()

    /* for {
        // ok 为 true 表示 channel 没有关闭, ok 为 false 表示 channel 已经关闭
        if data, ok := <- c; ok {
            fmt.Println("data: ", data) 
        } else {
            break
        }
    } */

    // 可以使用 range 代替不断迭代的操作的cahnnel
    for data := range c {
        fmt.Println("data: ", data)
    }

    fmt.Println("main goroutine 结束")

}

channel 与 select

单流程下,一个go只能监控一个 channel 状态,select 可以完成监控多个 channel 的状态。

package main

import (
    "fmt"
)

func method(c, quit chan int) {
    x, y := 1, 1

    for {
        select {
        case c <- x:
            // 如果 c 可写入,执行下面
            x = y
            y = x + y
        case <- quit:
            fmt.Println("quit.....")
            return 
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)

    go func() {
        for i := 0; i < 6; i++ {
            fmt.Println(<-c)
        }

        quit <- 0
    }()

    method(c, quit)
}

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章

1 2 3 4 5