golang的channel什么时候会阻塞?什么时候会恢复?

在 Go 语言中,channel 的发送和接收操作会根据其缓冲区的状态而阻塞或恢复。以下是详细说明:

1. 无缓冲 channel

  • 发送操作

    • 阻塞:发送数据时会阻塞,直到有另一个 goroutine 从该 channel 接收数据。
  • 接收操作

    • 阻塞:接收数据时会阻塞,直到有另一个 goroutine 向该 channel 发送数据。

2. 有缓冲 channel

  • 发送操作

    • 阻塞:当缓冲区已满时,发送操作会阻塞,直到有空间可以发送数据。
    • 恢复:当接收操作释放了空间(即接收数据),发送操作会恢复执行。
  • 接收操作

    • 阻塞:当缓冲区为空时,接收操作会阻塞,直到有数据可以接收。
    • 恢复:当发送操作添加了数据,接收操作会恢复执行。

示例代码

以下是一个示例,展示无缓冲和有缓冲 channel 的阻塞和恢复行为:

package main

import (
	"fmt"
	"time"
)

func main() {
	// 无缓冲 channel 示例
	ch1 := make(chan int)

	go func() {
		ch1 <- 1 // 发送数据,阻塞直到接收
		fmt.Println("Sent 1")
	}()

	// 主 goroutine 等待一秒再接收
	time.Sleep(1 * time.Second)
	fmt.Println("Received:", <-ch1) // 接收数据,解除阻塞

	// 有缓冲 channel 示例
	ch2 := make(chan int, 2) // 缓冲区大小为 2

	ch2 <- 1 // 不会阻塞
	ch2 <- 2 // 也不会阻塞

	go func() {
		ch2 <- 3 // 这里会阻塞,直到有接收
		fmt.Println("Sent 3")
	}()

	fmt.Println("Received from buffered channel:", <-ch2) // 接收,解除阻塞
	fmt.Println("Received from buffered channel:", <-ch2) // 继续接收
	time.Sleep(1 * time.Second) // 确保 goroutine 完成
}

总结

  • 无缓冲 channel:发送和接收操作会相互阻塞,直到对方准备好。
  • 有缓冲 channel:发送操作在缓冲区未满时不会阻塞;接收操作在缓冲区为空时会阻塞。
### GolangChannel 的用法与示例 ChannelGo 语言中的一个重要特性,用于在 Goroutines 之间安全地传递数据。它提供了一种简洁的方式来实现并发控制和通信。 #### 创建和初始化 Channel 可以使用 `make` 函数创建一个新的 channel。下面是一个简单的例子: ```go ch := make(chan int) ``` 此代码片段定义了一个整数类型的 channel[^1]。 #### 发送和接收数据 通过 `<-` 运算符可以在 channel发送或接收数据。以下是基本操作的例子: ```go // 向 channel 发送数据 ch <- value // 从 channel 接收数据 value := <-ch ``` 这些语句分别表示向 channel 发送数据和从 channel 获取数据的操作[^2]。 #### 使用带缓冲区的 Channel 无缓冲的 channel 只有当接收方准备好时才会继续执行;而带缓冲的 channel 则允许存储一定数量的数据项而不阻塞。可以通过指定第二个参数来设置缓冲大小: ```go bufferedCh := make(chan int, 10) ``` 这里创建了一个容量为 10 的带缓冲 channel[^3]。 #### 关闭 Channel 和遍历其值 关闭一个 channel 表明不会再有任何新的值被写入其中。这通常发生在生产者完成所有工作之后。消费者可以通过 range 循环自动处理直到 channel 被关闭为止的所有值: ```go close(ch) for val := range ch { fmt.Println(val) } ``` 注意,在多生产者的场景下应小心管理谁负责关闭 channel,以免引发 panic 错误。 #### Select 语句支持多个通道操作 Go 提供了 select 语句用来等待多个 communication 操作之一变得可用。这是一个典型的非阻塞 I/O 实现方式: ```go select { case msg1 := <-ch1: fmt.Println("received", msg1) case ch2 <- "ping": default: fmt.Println("no activity") } ``` 上述代码展示了如何监听来自不同 channels 的消息或者尝试发送一条新消息给某个 channel。 #### 完整示例:计数器应用 以下展示的是利用 goroutine 和 unbuffered channel 构建的一个简单计数器应用程序: ```go package main import ( "fmt" ) func counter(out chan<- int) { for i := 0; i < 100; i++ { out <- i // 将当前数值传送到 out channel } close(out) // 结束后关闭该 channel } func square(in <-chan int, out chan<- int) { for v := range in { // 遍历输入 channel 的每一个值 out <- v * v // 计算平方并传送至下一个 stage } close(out) // 处理完成后关闭 output channel } func printer(in <-chan int) { for v := range in { // 打印接收到的所有值 fmt.Println(v) } } func main() { naturals := make(chan int) squares := make(chan int) go counter(naturals) go square(naturals, squares) printer(squares) } ``` 在这个程序里,我们启动三个独立的任务——生成自然数序列、计算它们各自的平方以及打印最终的结果集。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值