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

相关推荐
入 梦皆星河2 分钟前
在 Ubuntu/Debian 上安装 Go
ubuntu·golang·debian
程序猿进阶5 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺10 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
zwjapple16 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five17 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
前端每日三省19 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
凡人的AI工具箱32 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
先天牛马圣体37 分钟前
如何提升大型AI模型的智能水平
后端
java亮小白199740 分钟前
Spring循环依赖如何解决的?
java·后端·spring
chnming19871 小时前
STL关联式容器之map
开发语言·c++