非缓冲通道
- 发送操作:
- 当向非缓冲通道发送数据时,如果没有其他 goroutine 在接收数据,发送操作会阻塞当前 goroutine,直到有其他 goroutine 准备好接收数据。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 10 // 发送操作,如果没有接收者,这里会阻塞
}()
num := <-ch
fmt.Println(num)
}
- 在上述代码中,如果没有
num := <-ch
这一行接收操作,ch <- 10
就会一直阻塞,导致程序死锁(因为 main 函数最后也会阻塞等待所有 goroutine 结束,而这个 goroutine 又在等待接收者)。
- 接收操作:
- 从非缓冲通道接收数据时,如果没有其他 goroutine 在发送数据,接收操作也会阻塞当前 goroutine,直到有数据被发送进来。如上述代码中,如果没有
ch <- 10
这一行发送数据,num := <-ch
就会阻塞。
- 非缓冲通道保证了发送和接收操作的同步性,数据从发送者直接传递到接收者,没有中间存储。
缓冲通道
- 发送操作:
- 向缓冲通道发送数据时,如果通道的缓冲区未满,发送操作不会阻塞,数据会被放入缓冲区。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
ch <- 10
ch <- 20
fmt.Println("Data sent to buffer successfully")
}
- 在这个例子中,通道
ch
的缓冲区大小为 2,所以可以连续发送两个数据而不阻塞。
- 当缓冲区已满时,再进行发送操作,就会阻塞当前 goroutine,直到有其他 goroutine 从通道中接收数据,腾出空间。比如在上述代码后再添加
ch <- 30
,此时就会阻塞。
- 接收操作:
- 从缓冲通道接收数据时,如果缓冲区中有数据,接收操作不会阻塞,会直接从缓冲区中取出数据。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
ch <- 10
ch <- 20
num1 := <-ch
fmt.Println(num1)
}
- 这里
num1 := <-ch
会直接从缓冲区取出第一个数据 10。
- 当缓冲区为空时,接收操作会阻塞当前 goroutine,直到有数据被发送到通道中。
两者区别总结
- 阻塞特性:
- 非缓冲通道在发送和接收时,如果没有对应的接收者或发送者,都会阻塞。
- 缓冲通道在缓冲区未满时发送不阻塞,缓冲区有数据时接收不阻塞,只有在缓冲区满时发送和缓冲区空时接收才会阻塞。
- 数据存储:
- 非缓冲通道没有内部缓冲区,数据直接从发送者传递到接收者。
- 缓冲通道有一个指定大小的内部缓冲区,可以临时存储数据,允许发送者在没有立即接收者的情况下发送一定数量的数据。