二十五、go语言的通道

目录

一、收发通信

二、将通道作为参数传递(读、写、读写)

三、select

1、先收到消息的先执行

2、一直没有收到消息退出通道

3、不知道何时退出情况下退出通道


go语言中的goroutine可以看成线程,但是又不能看成和其它语言一样的线程,因为在go语言中,goroutine之间的通信和其它语言不一样,例如如果在java中多条线程访问一个内存中数据在访问前需要进行加锁避免其它线程进入进行修改,本质上是通过共享了内存进行通信。而在go中是不一样的,在go中是"不要通过共享内存进行通信,而是通过通信来共享内存"

一、收发通信

复制代码
import (
	"fmt"
	"time"
)

func receiver(c chan string) {
	for msg := range c {
		fmt.Println(msg)
	}
}
func main() {
	message := make(chan string, 2)
	message <- "hello"
	message <- "world"
	close(message)
	fmt.Println("push two message to channel")
	time.Sleep(time.Second * 2)
	receiver(message)
}

结果在2s中后输出

hello

world

解释

上述案例中 receiver函数接收了一个通道为string值的参数,并且进行循环输出

main方法中,mak(chan string,2)声明创建长度为2的通道

message <- "" 往通道内存入消息

close关闭通道意味着不可以再通过这个通道发送消息

调用receiver并传入message参数

二、将通道作为参数传递(读、写、读写)

在参数中<-在chan左边意味着可读,在右边意味着可写,没有意味着可读可写

复制代码
func showReader(c <-chan string) {
	msg := <-c
	fmt.Println(msg)
}
func showWriter(c chan<- string) {
	c <- "hello world"
}

func showReaderAndWriter(c chan string) {
	msg := <-c
	fmt.Println(msg)
	c <- "hello world"
}

在reader函数中,c是什么我们只能输出什么

在writer函数中,c是什么我们可以修改

在readerAndWriter函数中,c是什么我们可以输出什么也可以指定c是什么

三、select

1、先收到消息的先执行
复制代码
func ping(c chan int) {
	time.Sleep(time.Second * 3)
	c <- 1
}
func ping2(c chan<- int) {
	time.Sleep(time.Second * 2)
	c <- 2
}
func main() {
	channel := make(chan int)
	channel2 := make(chan int)
	go ping(channel)
	go ping2(channel2)
	select {
	case msg := <-channel:
		fmt.Println(msg)
	case msg := <-channel2:
		fmt.Println(msg)
	}
}

结果:

2

解释L

在select中最先收到消息的执行后续代码,当创建两个ping方法时,ping sleep了3秒,ping2 sleep了2秒所以先执行了ping2

2、一直没有收到消息退出通道

当一直没有收到消息可以使用case <-time.After(time.Second * 3):

复制代码
import (
	"fmt"
	"time"
)

func ping(c chan int) {
	time.Sleep(time.Second * 5)
	c <- 1
}
func ping2(c chan<- int) {
	time.Sleep(time.Second * 4)
	c <- 2
}
func main() {
	channel := make(chan int)
	channel2 := make(chan int)
	go ping(channel)
	go ping2(channel2)
	select {
	case msg := <-channel:
		fmt.Println(msg)
	case msg := <-channel2:
		fmt.Println(msg)
	case <-time.After(time.Second * 3):
		fmt.Println("timeout")
	}

}
3、不知道何时退出情况下退出通道
复制代码
import (
	"fmt"
	"time"
)

func sender(c chan string) {
	t := time.NewTicker(1 * time.Second)
	for {
		c <- "hello world"
		<-t.C
	}
}
func main() {
	messages := make(chan string)
	stop := make(chan bool)
	go sender(messages)
	go func() {
		time.Sleep(2 * time.Second)
		fmt.Println("time up")
		stop <- true
	}()
	for {
		select {
		case status := <-stop:
			fmt.Println(status)
			return
		case msg := <-messages:
			fmt.Println(msg)
		}
	}
}

结果:

hello world

hello world

hello world

time up

true

解释:

创建了一个sender方法,使用time.NewTicker(1 * time.Second),每一秒往通道中传递一次值

复制代码
t := time.NewTicker(1 * time.Second)
for {
    c <- "hello world"
    <-t.C
}

在for循环中执行

复制代码
case msg := <-messages:
    fmt.Println(msg)

在接收到消息后执行,那么如果在没有手动终止程序的话就会一直执行

所以创建一个退出通道,当接收到true值时进行return,退出通道

复制代码
case status := <-stop:
			fmt.Println(status)
			return

在同时并发执行一个func,并让这个程序sleep 2秒后,在给通道stop值为true

复制代码
go func() {
		time.Sleep(2 * time.Second)
		fmt.Println("time up")
		stop <- true
	}()

所以在第一次并发调用

复制代码
go sender(messages)

执行了一次

复制代码
fmt.Println(msg)

此时func开始sleep 2秒,所以又执行了两次,当stop为ture时进行了return退出了通道

相关推荐
RickyWasYoung14 分钟前
【代码】matlab-遗传算法工具箱
开发语言·matlab
程序员蜗牛29 分钟前
9个Spring Boot参数验证高阶技巧,第8,9个代码量直接减半!
后端
yeyong30 分钟前
咨询kimi关于设计日志告警功能,还是有启发的
后端
库森学长36 分钟前
2025年,你不能错过Spring AI,那个汲取了LangChain灵感的家伙!
后端·openai·ai编程
Java水解1 小时前
Spring Boot 启动流程详解
spring boot·后端
学历真的很重要1 小时前
Claude Code Windows 原生版安装指南
人工智能·windows·后端·语言模型·面试·go
转转技术团队1 小时前
让AI成为你的编程助手:如何高效使用Cursor
后端·cursor
shellvon1 小时前
你怎么被识别的?从TLS到Canvas的设备追踪术
后端·算法
yinke小琪1 小时前
消息队列如何保证消息顺序性?从原理到代码手把手教你
java·后端·面试
土了个豆子的1 小时前
03.缓存池
开发语言·前端·缓存·visualstudio·c#