企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 信道(channel) 信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收; 所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的; 信道的零值为`nil`; ### channel声明 语法: ~~~go var a chan int // 申明 a := make(chan int) // 简洁语法 data := <- a // 读取信道 a a <- data // 写入信道 a ~~~ ### 死锁 * 当Go协程给一个信道发送数据时,如果没有其他 Go 协程来接收数据,程序就会在运行时触发 panic,形成死锁 * 当有Go协程等着从一个信道接收数据时,如果没有其他的 Go 协程向该信道写入数据,则会触发 panic * 向容量为 2 的缓冲信道写入 3 个字符串 ~~~go func main() { ch := make(chan string, 2) ch <- "naveen" ch <- "paul" ch <- "steve" fmt.Println(<-ch) fmt.Println(<-ch) } ~~~ ## 关闭信道和使用 for range 遍历信道 数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来; 当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭 ~~~ v, ok := <- ch ~~~ 如果成功接收信道所发送的数据,那么`ok`等于 true。而如果`ok`等于 false,说明我们试图读取一个关闭的通道。从关闭的信道读取到的值会是该信道类型的零值 示例: ~~~go func producer(chnl chan int) { for i := 0; i < 10; i++ { chnl <- i } close(chnl) } func main() { ch := make(chan int) go producer(ch) for { v, ok := <-ch if ok == false { break } fmt.Println("Received ", v, ok) } } ~~~ 使用range接收信道数据 ~~~go func producer(chnl chan int) { for i := 0; i < 10; i++ { chnl <- i } close(chnl) } func main() { ch := make(chan int) go producer(ch) for v := range ch { fmt.Println("Received ",v) } } ~~~ ## future模式 ~~~ func RunTask() chan string { result := make(chan string) go func() { fmt.Println("处理异步任务。。。") time.Sleep(2 * time.Second) result <- "success" }() return result } func TestFutureTaskStudy(t *testing.T) { result := RunTask() fmt.Println("main gorouting running") fmt.Println("task result:", <-result) } ~~~ ## 关闭channel v,ok <- ch; ok为bool值,true表示正常接受;false表示同道关闭 所有的channel接收者都会在channel关闭时,立刻从阻塞等待中返回且返回状态值为false;这个广播机制通常被用来向多个订阅者同时发送信号; 使用channel实现生产者消费者模式: ~~~ var wg sync.WaitGroup var messageQueue chan int = make(chan int) func ProducerMessage() { for i := 1; i <= 20; i++ { messageQueue <- i fmt.Println("生产者生产数据:", i) } close(messageQueue) wg.Done() } func ConsumerMessage1() { for { v, ok := <-messageQueue if !ok { fmt.Println("channel closed") break } fmt.Println("ConsumerMessage-1:", v) } } func ConsumerMessage2() { for { v, ok := <-messageQueue if !ok { fmt.Println("channel closed") break } fmt.Println("ConsumerMessage-2:", v) } } func TestMessageTopic(t *testing.T) { wg.Add(1) go ProducerMessage() go ConsumerMessage1() go ConsumerMessage2() wg.Wait() fmt.Println("main goroutine end") } ~~~ ***** 【知识点】 * 无缓冲信道的发送和接收过程是阻塞的 ~~~go func hello(done chan bool) { fmt.Println("hello go routine is going to sleep") time.Sleep(4 * time.Second) fmt.Println("hello go routine awake and going to write to done") done <- true } func main() { done := make(chan bool) fmt.Println("Main going to call hello go goroutine") go hello(done) <-done fmt.Println("Main received data") } ~~~