0
点赞
收藏
分享

微信扫一扫

Pr:首选项 - 音频

回溯 2024-09-11 阅读 34

无缓冲的 channel 和有缓冲的 channel 的区别?

在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。

  1. 无缓冲的 channel
package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个无缓冲的 channel
	ch := make(chan int)

	// 启动一个 goroutine 来接收数据
	go func() {
		// 接收数据之前会阻塞,直到 main goroutine 发送数据
		val := <-ch
		fmt.Println("接收到的数据:", val)
	}()

	// 模拟一些操作
	time.Sleep(1 * time.Second)

	// 发送数据到 channel,会阻塞直到接收方读取数据
	ch <- 42

	fmt.Println("数据已发送")
}

由于是无缓冲的 channel,main goroutine 在发送 42 时会阻塞,直到 goroutine 从 channel 中接收到这个值,程序才会继续执行。
2. 有缓冲的 channel

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个带有缓冲区大小为 2 的 channel
	ch := make(chan int, 2)

	// 发送两个数据到 channel
	ch <- 1
	fmt.Println("发送了数据 1")
	ch <- 2
	fmt.Println("发送了数据 2")

	// 此时,由于缓冲区还有空间,发送不会阻塞
	go func() {
		// 延迟读取,模拟一些操作
		time.Sleep(2 * time.Second)
		val := <-ch
		fmt.Println("接收到的数据:", val)
	}()

	// 继续发送数据
	time.Sleep(1 * time.Second)
	ch <- 3
	fmt.Println("发送了数据 3")

	// 接收数据
	time.Sleep(2 * time.Second)
	val := <-ch
	fmt.Println("接收到的数据:", val)
}

这里的 channel 有缓冲区大小为 2,因此前两个 ch <- 操作不会阻塞,因为缓冲区有足够空间。第三次发送数据时,如果缓冲区已满,发送方会阻塞,直到接收方读取数据并释放空间。

channel和select底层数据结构是怎样的?

type hchan struct {
    qcount   uint           // 通道中已经存在的数据个数
    dataqsiz uint           // 环形队列的大小
    buf      unsafe.Pointer // 环形队列的指针
    elemsize uint16         // 每个元素的大小
    closed   uint32         // 通道是否关闭
    sendx    uint           // 发送操作的索引
    recvx    uint           // 接收操作的索引
    recvq    waitq          // 等待接收的 Goroutine 队列
    sendq    waitq          // 等待发送的 Goroutine 队列
}

recvq/sendq:表示接收和发送操作等待的 Goroutine 队列。当 select 语句中有对通道的接收或发送操作时,如果通道未就绪,当前 Goroutine 会被加入相应的等待队列。

type scase struct {
    c    *hchan         // 指向通道的指针
    kind uint16         // 操作类型(发送、接收)
    pc   uintptr        // 程序计数器,用于跟踪执行位置
    elem unsafe.Pointer // 数据元素的指针,用于发送或接收操作
}

c:指向通道的指针,表示这个 case 监听哪个通道。
kind:表示操作类型,是发送、接收还是默认 case。
elem:存储数据的指针,用于发送或接收操作时的存取。

举报

相关推荐

0 条评论