您的位置:首页 > 健康 > 美食 > 工程招聘网站_纬天建筑工程信息资讯网_网络推广平台有哪些公司_百度seo查询系统

工程招聘网站_纬天建筑工程信息资讯网_网络推广平台有哪些公司_百度seo查询系统

2025/3/26 2:20:06 来源:https://blog.csdn.net/Freesia_2350/article/details/146462739  浏览:    关键词:工程招聘网站_纬天建筑工程信息资讯网_网络推广平台有哪些公司_百度seo查询系统
工程招聘网站_纬天建筑工程信息资讯网_网络推广平台有哪些公司_百度seo查询系统

Go 语言中的 channel 是基于 CSP(Communicating Sequential Processes)思想实现的通信机制。其核心理念是:

  • 通过通信共享内存,而不是通过共享内存通信
    也就是说,应当让不同 goroutine 之间通过传递数据来共享状态,避免直接共享内存而产生竞态问题。

  • FIFO 队列
    Channel 本身就是一个队列,按照先进先出的原则传递数据。

  • 阻塞与死锁

    • 无缓冲通道:发送操作和接收操作必须同时发生,否则将阻塞,容易发生死锁。
    • 有缓冲通道:有固定的缓冲区,当缓冲区满时发送操作阻塞;当缓冲区空时接收操作阻塞。
  • 数据类型限制
    每个 channel 只能存放同一种数据类型,且 channel 是引用类型,需要通过 make 进行初始化。

  • 同步通信与协程控制
    Channel 不仅用于数据传递,还可以用来实现不同 goroutine 之间的同步控制,比如等待子协程执行完毕再退出主协程。

  • 多路复用
    使用 select 语句可以同时监听多个 channel 的操作,实现非阻塞通信和超时控制。

1. 无缓冲通道的基本使用

知识点说明:

  • 无缓冲通道没有存储空间,发送和接收必须成对出现。当一方操作时,另一方必须就绪,否则就会阻塞。
  • 该示例展示了如何在一个 goroutine 中发送数据,通过主 goroutine 接收数据。

代码示例:

// 文件名: 03channel_unbuffered.go
package mainimport ("fmt""time"
)// 发送数据到无缓冲通道
func senddata(ch chan int) {ch <- 1fmt.Println("往通道里存放数据 1")ch <- 2fmt.Println("往通道里存放数据 2")
}func main() {// 创建无缓冲通道(容量为0)ch1 := make(chan int)// 启动一个 goroutine 发送数据到通道go senddata(ch1)// 主 goroutine 休眠一段时间以等待子协程发送数据time.Sleep(3 * time.Second)// 按照先进先出的顺序接收数据data := <-ch1fmt.Println("拿到通道数据:", data)data2 := <-ch1fmt.Println("拿到通道数据:", data2)// 若再接收数据,由于通道中没有数据,将会阻塞// data3 := <-ch1// fmt.Println("第三次拿到通道数据:", data3)
}

2. 利用无缓冲通道实现协程同步

知识点说明:

  • 协程同步:在 Go 中,主协程退出会导致整个程序结束。如果想等待子协程执行完毕,可以利用无缓冲通道作为信号量。
  • 使用空结构体(struct{})作为信号传递,因为它不占用内存空间。

代码示例:

// 文件名: 03channel_sync.go
package mainimport ("fmt""time"
)// 定义空结构体,作为信号传递
type none struct{}// 发送信号到无缓冲通道,模拟子协程任务完成
func senddata(ch chan none) {fmt.Println("this is senddata")time.Sleep(2 * time.Second)ch <- none{} // 发送一个空结构体信号
}func main() {fmt.Println("start....")// 创建无缓冲通道(类型为 none)ch1 := make(chan none)// 启动子协程执行任务go senddata(ch1)// 主协程等待接收到信号后再继续<-ch1fmt.Println("end....")
}

3. 通道的关闭与数据消费

知识点说明:

  • 通道关闭:发送方完成数据发送后,通过 close(ch) 关闭通道,可以防止消费者无限等待数据。
  • 数据消费:接收方可以通过三种方式判断通道是否关闭:
    1. 判断读取到的值是否为数据类型的零值;
    2. 使用 value, ok := <-ch 判断;
    3. 使用 range 自动遍历,直到通道关闭。

代码示例:

// 文件名: 03channel_close.go
package mainimport ("fmt"
)// 发送数据并关闭通道
func sendata(ch chan string) {for i := 0; i < 3; i++ {ch <- fmt.Sprintf("发送数据%d", i)}// 打印提示并关闭通道,确保接收方不会因无数据而死锁defer fmt.Println("发送数据完毕")defer close(ch)
}func main() {ch1 := make(chan string)go sendata(ch1)// 方式1:读取数据后判断零值(适用于数据类型零值不会作为有效数据的情况)/*for {data := <-ch1if data == "" { // 字符串的零值为 ""break}fmt.Println("从通道中获取到的数据:", data)}*/// 方式2:使用 value, ok 判断通道是否关闭/*for {data, ok := <-ch1if !ok {break}fmt.Println("2从通道中获取到的数据:", data)}*/// 方式3:使用 range 遍历通道(推荐方式)for value := range ch1 {fmt.Println("3从通道中获取到的数据:", value)}
}

4. 有缓冲通道的使用

知识点说明:

  • 有缓冲通道:在创建通道时指定缓冲区大小,允许发送者先存储一定数量的数据而不必等待接收者。
  • 当缓冲区满时,发送操作将阻塞;当缓冲区空时,接收操作将阻塞。
  • 适用于生产者和消费者速度不一致的场景。

代码示例:

// 文件名: 03channel_buffered.go
package mainimport ("fmt""time"
)// 向有缓冲通道发送数据
func senddata(ch chan string) {for i := 0; i <= 10; i++ {ch <- fmt.Sprintf("放入数据%d", i)fmt.Printf("往通道放入数据%d\n", i)}// 发送完成后关闭通道defer close(ch)
}func main() {// 创建一个带缓冲的通道,缓冲区大小为 6ch1 := make(chan string, 6)go senddata(ch1)// 等待一定时间,保证数据发送到缓冲区time.Sleep(2 * time.Second)// 使用 range 循环读取数据,直到通道关闭for v := range ch1 {fmt.Println("读取通道数据:", v)}
}

5. 使用 select 监听多个通道

知识点说明:

  • select 语句:允许同时监听多个通道操作,当其中一个通道操作准备就绪时执行对应的 case;若多个同时就绪,则随机选择。
  • 超时控制:结合 time.After 实现超时等待,避免长时间阻塞。
  • default 分支:用于非阻塞检测,如果没有通道操作准备好,则执行 default 分支。

代码示例:

// 文件名: 03channel_select.go
package mainimport ("fmt""time"
)func main() {ch1 := make(chan int)ch2 := make(chan int)ch3 := make(chan int)// 分别启动三个 goroutine,在不同时间发送数据到各自的通道go func() {time.Sleep(10 * time.Second)ch1 <- 1}()go func() {time.Sleep(2 * time.Second)ch2 <- 2}()go func() {time.Sleep(3 * time.Second)ch3 <- 3}()// 循环监听 3 次 select 中的不同通道或超时/default 分支for i := 0; i < 3; i++ {select {case msg := <-ch1:fmt.Println("接收到数据 from ch1:", msg)case msg := <-ch2:fmt.Println("接收到数据 from ch2:", msg)case msg := <-ch3:fmt.Println("接收到数据 from ch3:", msg)// 超时控制case <-time.After(1 * time.Second):fmt.Println("设置超时时间!timeout")// 非阻塞操作default:fmt.Println("没有消息准备好")}}
}

总结

本次整理中,我们按照以下知识点对 channel 的使用进行了归类整理:

  1. 无缓冲通道的基本使用:展示了发送和接收操作必须同步进行的特性。
  2. 利用无缓冲通道实现协程同步:通过空结构体信号,让主协程等待子协程执行完毕。
  3. 通道的关闭与数据消费:演示了如何关闭通道以及用 range 自动遍历数据,避免因死锁而阻塞。
  4. 有缓冲通道的使用:展示了带缓冲区通道在生产者与消费者场景中的使用,以及缓冲满、缓冲空时的阻塞机制。
  5. select 监听多个通道:通过 select 实现多路复用、超时控制以及非阻塞检测。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com