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

相关推荐
美若黎明@27 分钟前
C# 路径操作
开发语言·c#
Dovir多多1 小时前
渗透测试入门学习——php与mysql数据库连接、使用session完成简单的用户注册、登录
前端·数据库·后端·mysql·安全·html·php
难以触及的高度1 小时前
source ~/.bash_profile有什么用
开发语言·bash
Flying_Fish_roe1 小时前
Spring Boot-WebSocket相关问题
spring boot·后端·websocket
骆晨学长1 小时前
基于springboot学生健康管理系统的设计与实现
java·开发语言·spring boot·后端·spring
骆晨学长1 小时前
基于Springboot的医疗健康助手开题报告
java·spring boot·后端
我是小酒1 小时前
掌握 Spring:从新手到高手的常见问题汇总
java·后端·spring·springboot
if时光重来1 小时前
springboot项目实现导出excel动态设置表头
spring boot·后端·excel
code.song1 小时前
校园社团|基于springBoot的校园社团信息管理系统设计与实现(附项目源码+论文+数据库)
数据库·spring boot·后端
白总Server1 小时前
php语言基本语法
开发语言·ide·后端·golang·rust·github·php