当前位置:首页 > 科技  > 软件

深入Go原理:协程间通信基础Chan

来源: 责编: 时间:2024-05-29 08:53:54 76观看
导读在 Go 语言中,chan(通道)是用于在不同 goroutine 之间进行通信和同步的重要机制。它的设计和实现允许在并发编程中安全、有效地传递数据。以下是 chan 的工作原理和实现细节基本概念通道类型通道有类型,指定了通道能够传

在 Go 语言中,chan(通道)是用于在不同 goroutine 之间进行通信和同步的重要机制。它的设计和实现允许在并发编程中安全、有效地传递数据。以下是 chan 的工作原理和实现细节lCI28资讯网——每日最新资讯28at.com

基本概念

通道类型

通道有类型,指定了通道能够传递的数据类型。例如,chan int 是一个只能传递整数的通道。lCI28资讯网——每日最新资讯28at.com

无缓冲通道

没有缓冲区的通道,发送和接收操作是同步的,即发送操作会阻塞直到有接收操作发生。lCI28资讯网——每日最新资讯28at.com

有缓冲通道

具有一定缓冲区的通道,发送操作在缓冲区未满时不会阻塞,直到缓冲区满时才会阻塞。lCI28资讯网——每日最新资讯28at.com

通道的内部结构

通道在内部是通过 hchan 结构体来实现的。这个结构体包含了通道的基本信息和状态lCI28资讯网——每日最新资讯28at.com

type hchan struct {    qcount   uint           // 缓冲区中数据的数量    dataqsiz uint           // 缓冲区的大小    buf      unsafe.Pointer // 缓冲区指针    elemsize uint16         // 元素的大小    closed   uint32         // 通道是否关闭    sendx    uint           // 发送操作的索引    recvx    uint           // 接收操作的索引    recvq    waitq          // 等待接收的 goroutine 队列    sendq    waitq          // 等待发送的 goroutine 队列    lock     mutex          // 保护通道的互斥锁}

发送和接收操作

无缓冲通道

发送操作

如果没有接收者,发送方会阻塞,直到有接收方开始接收。lCI28资讯网——每日最新资讯28at.com

接收操作

如果没有发送者,接收方会阻塞,直到有发送方开始发送。lCI28资讯网——每日最新资讯28at.com

有缓冲通道

发送操作

如果缓冲区未满,数据直接写入缓冲区。若缓冲区已满,发送方会阻塞,直到有空间可用。lCI28资讯网——每日最新资讯28at.com

接收操作

如果缓冲区不为空,数据直接从缓冲区读取。若缓冲区为空,接收方会阻塞,直到有数据可读。lCI28资讯网——每日最新资讯28at.com

通道的同步机制

通道的发送和接收操作都是原子性的,并且由互斥锁保护。这确保了多个 goroutine 同时操作通道时不会发生竞态条件。lCI28资讯网——每日最新资讯28at.com

互斥锁(Mutex)

每个通道都有一个互斥锁,用于保护通道的状态和数据。lCI28资讯网——每日最新资讯28at.com

等待队列(Wait Queue)

通道维护两个等待队列,一个用于等待接收的 goroutine,一个用于等待发送的 goroutine。当发送或接收操作不能立即完成时,goroutine 会被加入相应的等待队列中。lCI28资讯网——每日最新资讯28at.com

通道关闭

关闭通道

通过调用 close(chan) 可以关闭通道。关闭操作会设置通道的 closed 标志,并唤醒所有在通道上阻塞的发送和接收操作。lCI28资讯网——每日最新资讯28at.com

关闭后的操作

向已关闭的通道发送数据会引发 panic,从已关闭的通道接收数据会立即返回零值。lCI28资讯网——每日最新资讯28at.com

实现细节

以下是通道发送和接收操作的一些实现细节lCI28资讯网——每日最新资讯28at.com

发送操作

chan send 检查通道是否关闭,如果没有接收者且缓冲区未满,数据会被直接写入缓冲区,否则会阻塞当前 goroutine 并将其加入 sendq。lCI28资讯网——每日最新资讯28at.com

接收操作

chan recv 检查通道是否关闭或缓冲区是否为空,如果有数据则直接返回,否则阻塞当前 goroutine 并将其加入 recvq。lCI28资讯网——每日最新资讯28at.com

总结

Go 语言中的通道通过上述机制实现了 goroutine 之间的安全、高效通信。通道的设计考虑了并发编程中的同步问题,通过缓冲机制和等待队列的管理,使得数据传递和同步操作都能高效地进行。lCI28资讯网——每日最新资讯28at.com

例子

在 Go 语言中,可以通过 make 函数来定义通道。根据是否指定缓冲区大小,可以创建无缓冲区通道和有缓冲区通道。以下是具体的定义和示例:lCI28资讯网——每日最新资讯28at.com

无缓冲区通道

无缓冲区通道是指在没有缓冲区的情况下,发送和接收操作是同步的。发送操作会一直阻塞,直到有接收者接收数据。lCI28资讯网——每日最新资讯28at.com

定义无缓冲区通道
ch := make(chan int)
示例
package mainimport (    "fmt")func main() {    ch := make(chan int)    // 启动一个 goroutine 发送数据    go func() {        ch <- 42 // 发送操作会阻塞,直到有接收者    }()    // 接收数据    value := <-ch    fmt.Println(value) // 输出: 42}

在这个例子中,ch 是一个无缓冲区通道,发送操作 ch <- 42 会阻塞,直到主 goroutine 执行 <-ch 接收数据。lCI28资讯网——每日最新资讯28at.com

有缓冲区通道

有缓冲区通道允许在缓冲区未满时发送操作不会阻塞,直到缓冲区满时才会阻塞。lCI28资讯网——每日最新资讯28at.com

定义有缓冲区通道
ch := make(chan int, 3) // 创建一个缓冲区大小为 3 的通道
示例
package mainimport (    "fmt")func main() {    ch := make(chan int, 3) // 定义缓冲区大小为 3 的通道    // 发送数据到通道,不会阻塞    ch <- 1    ch <- 2    ch <- 3    // 缓冲区已满,下面的发送操作会阻塞,直到有接收者    go func() {        ch <- 4    }()    // 接收数据    fmt.Println(<-ch) // 输出: 1    fmt.Println(<-ch) // 输出: 2    fmt.Println(<-ch) // 输出: 3    fmt.Println(<-ch) // 输出: 4}

在这个例子中,ch 是一个有缓冲区通道,缓冲区大小为 3。前 3 个发送操作不会阻塞,直到缓冲区满后,第 4 个发送操作会阻塞,直到有接收者开始接收数据。lCI28资讯网——每日最新资讯28at.com

总结

通过 make(chan T) 可以创建无缓冲区通道,通过 make(chan T, capacity) 可以创建有缓冲区通道。无缓冲区通道在发送和接收操作上是同步的,而有缓冲区通道允许在缓冲区未满时进行非阻塞的发送操作。通过以上示例,可以清晰地看到两种通道的行为差异。lCI28资讯网——每日最新资讯28at.com

select

在 Go 语言中,select 语句用于处理多个通道的通信操作。它的作用是让 goroutine 可以同时等待多个通道操作(发送或接收),并在其中任何一个通道操作完成时执行相应的分支代码。select 语句的使用使得在处理并发编程时更加灵活和高效。lCI28资讯网——每日最新资讯28at.com

select 语句的基本用法

select 语句的语法与 switch 语句类似,但它专门用于通道操作。每个 case 分支包含一个通道操作(发送或接收),select 会选择其中一个已准备好的通道操作进行处理。lCI28资讯网——每日最新资讯28at.com

语法结构

select {case expr1:    // 如果 expr1 通道操作可以进行,则执行此分支case expr2:    // 如果 expr2 通道操作可以进行,则执行此分支default:    // 如果没有任何通道操作可以进行,则执行此分支}

示例:使用 select 同时等待多个通道操作

以下是一个使用 select 语句的示例:lCI28资讯网——每日最新资讯28at.com

package mainimport (    "fmt"    "time")func main() {    ch1 := make(chan string)    ch2 := make(chan string)    // 启动第一个 goroutine    go func() {        time.Sleep(2 * time.Second)        ch1 <- "message from ch1"    }()    // 启动第二个 goroutine    go func() {        time.Sleep(1 * time.Second)        ch2 <- "message from ch2"    }()    for i := 0; i < 2; i++ {        select {        case msg1 := <-ch1:            fmt.Println(msg1)        case msg2 := <-ch2:            fmt.Println(msg2)        }    }}

在这个例子中,有两个通道 ch1 和 ch2,每个通道都在不同的 goroutine 中发送消息。select 语句使得主 goroutine 可以同时等待两个通道的消息,并在任意一个通道接收到消息时执行相应的分支。lCI28资讯网——每日最新资讯28at.com

default 分支

如果在 select 语句中添加了 default 分支,当所有通道操作都无法立即进行时,会执行 default 分支。这样可以避免 select 语句阻塞。lCI28资讯网——每日最新资讯28at.com

示例:带有 default 分支的 select

package mainimport (    "fmt"    "time")func main() {    ch := make(chan string)    go func() {        time.Sleep(2 * time.Second)        ch <- "message"    }()    for {        select {        case msg := <-ch:            fmt.Println(msg)            return        default:            fmt.Println("No message received, doing other work")            time.Sleep(500 * time.Millisecond)        }    }}

在这个例子中,如果通道 ch 上没有消息可接收,select 会执行 default 分支,打印一条消息并继续执行其他工作。lCI28资讯网——每日最新资讯28at.com

总结

select 语句是 Go 语言中处理并发编程的重要工具,通过它可以同时等待多个通道操作并在其中一个操作完成时进行相应处理。select 提供了一种灵活且高效的方式来处理多个通道之间的通信,使得并发程序的设计更加简洁和直观。lCI28资讯网——每日最新资讯28at.com

等待多个通道的逻辑

在 Go 语言的 select 语句中,如果有多个通道操作同时准备就绪(即都可以进行),Go 运行时会从这些通道操作中随机选择一个执行。一旦某个通道操作被选中并执行,其它通道的等待操作将不会继续进行。每次执行 select 语句时都会重新评估所有通道操作。lCI28资讯网——每日最新资讯28at.com

示例:多个通道同时就绪

为了更好地理解这个机制,以下是一个示例,展示当多个通道同时准备就绪时,select 语句的行为:lCI28资讯网——每日最新资讯28at.com

package mainimport (    "fmt"    "time")func main() {    ch1 := make(chan string)    ch2 := make(chan string)    ch3 := make(chan string)    go func() {        time.Sleep(1 * time.Second)        ch1 <- "message from ch1"    }()    go func() {        time.Sleep(1 * time.Second)        ch2 <- "message from ch2"    }()    go func() {        time.Sleep(1 * time.Second)        ch3 <- "message from ch3"    }()    for i := 0; i < 3; i++ {        select {        case msg1 := <-ch1:            fmt.Println(msg1)        case msg2 := <-ch2:            fmt.Println(msg2)        case msg3 := <-ch3:            fmt.Println(msg3)        }    }}

在这个示例中,有三个通道 ch1, ch2, 和 ch3,每个通道在 1 秒后发送一个消息。因为所有通道在同一时间准备就绪,select 语句将从中随机选择一个进行处理,并打印相应的消息。每次循环都会重新评估所有通道。lCI28资讯网——每日最新资讯28at.com

结论

当 select 语句等待多个通道时,如果其中一个通道操作可以进行,其它通道的操作不会继续等待,而是等待下一次 select 语句的评估。每次 select 语句执行时都会重新评估所有通道操作,并选择其中一个可以进行的操作。如果多个通道同时就绪,select 会随机选择其中一个进行处理。lCI28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-91354-0.html深入Go原理:协程间通信基础Chan

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: SpringBoot优雅定制接口参数格式转换

下一篇: SpringBoot的自动装配,你学会了吗?

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 多线程开发带来的问题与解决方法

    多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 破圈是B站头上的紧箍咒

    破圈是B站头上的紧箍咒

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之每年的暑期档都少不了瞄准追剧女孩们的古偶剧集,2021年有优酷的《山河令》,2022年有爱奇艺的《苍兰诀》,今年却轮到小破站抓住了追
  • 共享单车的故事讲到哪了?

    共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 签约井川里予、何丹彤,单视频点赞近千万,MCN黑马永恒文希快速崛起!

    签约井川里予、何丹彤,单视频点赞近千万,MCN黑马永恒文希快速崛起!

    来源:视听观察永恒文希传媒作为一家MCN公司,说起它的名字来,可能大家会觉得有点儿陌生,但是说出来下面一串的名字之后,或许大家就会感到震惊,原来这么多网红,都签约这家公司了。根
  • 东方甄选单飞:有些鸟注定是关不住的

    东方甄选单飞:有些鸟注定是关不住的

    作者:彭宽鸿来源:华尔街科技眼&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;东方甄选创始人俞敏洪带队的&ldquo;7天甘肃行&rdquo;直播活动已在近日顺利收官。成立后一
  • 消息称小米汽车开始筛选交付中心:需至少120个车位

    消息称小米汽车开始筛选交付中心:需至少120个车位

    IT之家 7 月 7 日消息,日前,有微博简介为“汽车行业从业者、长三角一体化拥护者”的微博用户 @长三角行健者 发文表示,据经销商集团反馈,小米汽车目前
  • iQOO Neo8 Pro抢先上架:首发天玑9200+ 安卓性能之王

    iQOO Neo8 Pro抢先上架:首发天玑9200+ 安卓性能之王

    经过了一段时间的密集爆料,昨日iQOO官方如期对外宣布:将于5月23日推出全新的iQOO Neo8系列新品,官方称这是一款拥有旗舰级性能调校的作品。随着发布时
  • 英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    据10 月 30 日外媒 TheVerge 消息报道,英特尔 Xe HPG Arc Alchemist 的正面实被曝光,不仅拥有 512 EU 版显卡,还拥有 128EU 的单风扇版本。另外,这款显卡 PCB
Top