二十五、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退出了通道

相关推荐
ShadowYD4 小时前
Agent Loop:一个让 AI 编程 Agent 从"会写代码"进化到"闭环交付"的开源 Skill(适配国内外主流 Code Agent)
后端
考虑考虑5 小时前
nohup启动java程序
后端·自动化运维
aramae6 小时前
模拟实现strcmp()(C语言)
c语言·开发语言·后端
泡海椒7 小时前
PDF 表格样式优化:jquick-pdf 边框、圆角、背景色
java·开发语言·pdf
Beyond_System|系统之外8 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
根目录下的猫8 小时前
RK3588适配的轻量级AI模型推荐
人工智能·后端·python·目标检测
景熙55238 小时前
15.Java 8 Stream 流入门到实战
java·开发语言·数据结构
星栈9 小时前
用 Rust 写 Agent 服务 -- adk-rust 上手记
后端·agent
杨运交9 小时前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring