channel

channel


2023年12月14日发(作者:现在最新款的手机是哪一款)

channel

channel的基本介绍

channel的本质是一个数据结构队列

数据是先进先出 FIFO

线程安全,多goroutine访问时,不需要加锁,就是说channel本身是线程安全的

channel是由类型的,一个string的channel只能存放string类型数据

无缓冲的channel关闭后,再往外读数据读到的是该管道数据类型的初始值

有缓冲的channel的channel关闭后,如果管道内还有未被读出来的数据,可以继续读出来

判断管道是否关闭

if num,ok := <-ch;ok{

//如果ok为false,定是管道已经关闭了

}

使用for range则无需关心关闭的细节,go已经帮我们做好了,一旦检测到关闭,

会自己停止,但是程序的某一处一定要有关闭管道的操作,否则会报死锁

双向channel可以赋值给单项channel,反过来则不行

package main

import (

"fmt"

)

func send(out chan<- int) {

out <- 89

close(out)

}

func recv(in <-chan int) {

n := <-in

n("读到", n)

}

func main() {

ch := make(chan int)

go func() {

send(ch)

}()

recv(ch)

}

定义/声明

var intChan chan int //intChan用于存放int数据

var mapChan chan map[int]string //mapChan用于存放map[int]string类型

var perChan chan Person

var perChan2 chan *Person

1. channel是引用类型

2. channel必须初始化才能写入数据,即make后才能使用

3. 管道是由类型的,intChan只能写入整数int

package main

import "fmt"

func main(){

var intChan chan int

intChan = make(chan int, 3)

("intChan的值=%v intChan本身的地址=%pn",intChan, &intChan)

//intChan的值=0xc00007a080 intChan本身的地址=0xc000006028

intChan<- 10

num:=211

//向管道写入数据

intChan<- num

//看看管道的长度和cap(容量)

("channel len=%v cap=%v n",len(intChan),cap(intChan))

//从管道中读取数据

var num2 int

num2 = <-intChan

range n("num2=", num2)

("channel len=%v cap=%v n",len(intChan),cap(intChan))

<-intChan //直接取值不接收

//在没有下,取完后继续取回报错

}

如果使用空interface类型的管道,取出来的结构体数据是 接口类型,需要类型断言才能使用

package main

import "fmt"

type Cat struct{ Name string }

func main() {

var catChan chan interface{}

catChan = make(chan interface{}, 10)

cat := Cat{"小花猫"}

catChan <- cat

newCat := <-catChan //是错的编译的时候编译器会认为newCat是一个接口

c := newCat.(Cat) //使用类型断言转换之后可以正常使用

n() //小花猫

}

channel的关闭

channel一旦关闭只能读,不能写

package main

import "fmt"

func main(){

intChan := make(chan int, 3)

intChan<- 100

intChan<- 200

close(intChan) //close channel此时只能读不能写

}

channel的遍历

package main

import "fmt"

func main() {

intChan := make(chan int, 100)

for i := 0; i < 100; i++ {

intChan <- i * 2 //放入100个数据到管段

}

//遍历管道要使用fo range的方式去遍历,普通遍历不可以,因为每取一次容量会减少

close(intChan) // 在遍历时,如果管道没有关闭,则会出现deadlock的错误

for v := range intChan {

n("v=", v)

}

}

channel支持val,ok:= <-intChan这种方法

intChan := make(chan int, 100)

for i := 0; i < 100; i++ {

intChan <- i * 2 //放入100个数据到管段

}

if v, ok := <- intChan;ok{ //成功取到值ok为true否则为false

(v)

}

管道阻塞的机制

如果编译器运行,发现一个管道只有写,没有读,则该管道会阻塞

写管道和读管道的频率不一致,无所谓。

判断管道是否关闭

for{

if v, isClose := <- intChan;isClose{ //通过这种方式判断管道已经关闭

break

}

}只读和只写类型的管道

var wChan chan <-int

var rChan <-chan int

传统的方法在遍历管道时,如果不关闭会阻塞而导致deadlock,而使用select可以解决从管道取数据的阻塞问题

select 的用法有点类似 switch 语句,但 select 不会有输入值而且只用于信道操作。select 用于从多个发送或接收信道操作中进行选择,语句会阻塞直到其中有

信道可以操作,如果有多个信道可以操作,会随机选择其中一个 case 执行。

看下例子

func service1(ch chan string) {

(2 * )

ch <- "from service1"

}

func service2(ch chan string) {

(1 * )

ch <- "from service2"

}

func main() {

ch1 := make(chan string)

ch2 := make(chan string)

go service1(ch1)

go service2(ch2)

select { // 会发送阻塞

case s1 := <-ch1:

n(s1)

case s2 := <-ch2:

n(s2)

}

}

输出:from service2 上面的例子执行到 select 语句的时候回发生阻塞,main 协程等待一个 case 操作可执行,很明显是 service2 先准备好读取的数据(休眠

1s),所以输出 from service2。 看下在两种操作都准备好的情况:

func service1(ch chan string) {

//(2 * )

ch <- "from service1"

}

func service2(ch chan string) {

//(1 * )

ch <- "from service2"

}

func main() {

ch1 := make(chan string)

ch2 := make(chan string)

go service1(ch1)

go service2(ch2)

(2*)

select {

case s1 := <-ch1:

n(s1)

case s2 := <-ch2:

n(s2)

}

}

//我们把函数里的延时注释掉,主函数 select 之前加 2s 的延时以等待两个信道的数据准备好,select 会随机选取其中一个 case 执行,所以输出也是随机的。

与 switch 语句类似,select 也有 default case,是的 select 语句不在阻塞,如果其他信道操作还没有准备好,将会直接执行 default 分支。

func service1(ch chan string) {

ch <- "from service1"

}

func service2(ch chan string) {

ch <- "from service2"

}

func main() {

ch1 := make(chan string)

ch2 := make(chan string)

go service1(ch1)

go service2(ch2)

select { // ch1 ch2 都还没有准备好,直接执行 default 分支

case s1 := <-ch1:

n(s1) case s2 := <-ch2:

n(s2)

default:

n("no case ok")

}

}

添加超时时间

有时候,我们不希望立即执行 default 语句,而是希望等待一段时间,若这个时间段内还没有可操作的信道,则执行规定的语句。可以在

case 语句后面设置超时时间。

func service1(ch chan string) {

(5 * )

ch <- "from service1"

}

func service2(ch chan string) {

(3 * )

ch <- "from service2"

}

func main() {

ch1 := make(chan string)

ch2 := make(chan string)

go service1(ch1)

go service2(ch2)

select { // 会发送阻塞

case s1 := <-ch1:

n(s1)

case s2 := <-ch2:

n(s2)

case <-(2*): // 等待 2s

n("no case ok")

}

}

goroutine中使用recover,必须定义在被协程调用的函数中才行,在main函数中捕获不到

func test(){

defer func(){

if err := recover(); err!=nil{

n("test() 发生错误",err)

}

}()

var myMap map[int]string

myMap[0] = "golang" //error 这里没有make直接使用map会抛出异常

}


发布者:admin,转转请注明出处:http://www.yc00.com/num/1702523930a1228332.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信