golang select default continue_详解golang的数据类型和底层实现一

39d0899386689c8a9209496125eb086e.png

golang的引用类型的数据类型有三个chan、slice、map,虽然golang的函数的参数都是值传递,传递引用类型参数只会拷贝参数的变量,实际引用的地址仍然是一个,所以会改变函数外部的变量的值,产生变量逃逸到堆的现象。废话优点多了,今天看一眼golang的chan类型,chan类型是设计用来做goroutine通信的,类似unix的管道,如果了跨进程的通信还是用分布式来解决比较好,chan解决的是goroutine之间的通信。先看一下源码src/runtime/chan.go:hchan 32行,我的版本是1.13.5

type hchan struct {qcount uint // total data in the queue 剩余空间dataqsiz uint // size of the circular queue 队列长度    buf unsafe.Pointer // points to an array of dataqsiz elements 队列指针    elemsize uint16 //元素大小    closed uint32 //chan是否已经关闭    elemtype *_type // element type 元素类型    sendx uint // send index 写入新的元素存放位置    recvx uint // receive index 读取元素的位置    recvq waitq // list of recv waiters 等待读的goroutine队列    sendq waitq // list of send waiters 等待写的goroutine队列  // lock protects all fields in hchan, as well as several  // fields in sudogs blocked on this channel.  //  // Do not change another G's status while holding this lock  // (in particular, do not ready a G), as this can deadlock  // with stack shrinking.  lock mutex //互斥锁}

基本上是实现了一个环形队列,如果是缓冲队列环形队列作为缓冲区,队列的长度make(chan string, len)时由len指定,就是可以缓冲多少个元素,qcount标识还有多少个空闲的缓冲位置,buf指针指向队列的地址,sendx是新写入元素写入的队列的下标,recvx是下次读出数据的队列的下标。

chan通过两个队列和一个互斥锁实现不同goroutine的同步。首先如果chan的缓冲区满了,读写goroutine都会阻塞,会分别在recvq和sendq两个队列里挂起等待被唤醒;在有新的数据写入chan时recvq队列等待的读goroutine会被唤起,同样写队列会在chan缓冲有空位的时候唤起;无缓冲的chan的qcount=0,这时recvq和sendq肯定有一个队列是空的一个不是空的,因为没有位置区缓冲,只能在写入时候唤起读去读出来,同样写也只能在有读队列在等待的情况下写入,否则会panic。同一个chan只能允许一个goroutine读写是lock mutex互斥锁实现的。

chan结构体包含定义的元素的数据格式elemtype,只能存储指定类型的值;elemsize是元素的大小,用于找到buf中的位置。

初始化chan,make(chan string, len)len执行缓冲长度,string指定元素类型,buf大小由长度和元素大小共同决定;下面函数基本上就是创建hchan的数据结构和类型内存安全的判断

func makechan(t *chantype, size int) *hchan {        elem := t.elem        // compiler checks this but be safe.        if elem.size >= 1<<16 {        throw("makechan: invalid channel element type")        }        if hchanSize%maxAlign != 0 || elem.align > maxAlign {        throw("makechan: bad alignment")        }        mem, overflow := math.MulUintptr(elem.size, uintptr(size))        if overflow || mem > maxAlloc-hchanSize || size < 0 {        panic(plainError("makechan: size out of range"))        }        // Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.        // buf points into the same allocation, elemtype is persistent.        // SudoG's are referenced from their owning thread so they can't be collected.        // TODO(dvyukov,rlh): Rethink when collector can move allocated objects.        var c *hchan        switch {        case mem == 0:        // Queue or element size is zero.        c = (*hchan)(mallocgc(hchanSize, nil, true))        // Race detector uses this location for synchronization.        c.buf = c.raceaddr()        case elem.ptrdata == 0:        // Elements do not contain pointers.        // Allocate hchan and buf in one call.        c = (*hchan)(mallocgc(hchanSize+mem, nil, true))        c.buf = add(unsafe.Pointer(c), hchanSize)        default:        // Elements contain pointers.        c = new(hchan)        c.buf = mallocgc(mem, elem, true)        }        c.elemsize = uint16(elem.size)        c.elemtype = elem        c.dataqsiz = uint(size)        if debugChan {        print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "")        }        return c}

写chan就是先判断等待队列是否空,不空就从recvq取出一个goroutine把数据写入,唤醒这个goroutine;如果没有等待的读groutine,就写入缓冲区,缓冲区没有位置就把写入数据写入当前goroutine在sendq排队挂起等待被唤起。

读chan同样,先读缓冲区数据,缓冲区数据,判断sendq是否为空,不空则把goroutine唤起写入缓冲,读结束;没有数据可读则加入recvq队列挂起等待唤起。

关闭chan,会清理recvq和sendq的队列;sendq队列的goroutine会panic,recvq队列的goroutine写入的数据设置为空就是nil;所以关闭的chan继续写入会panic,但是还是能读关闭chan的buf数据。

常用使用demo:

单向队列

func main() {    var c = make(chan int, 10)    writeChan(c)    readChan(c)}func readChan(flow 

多路io监听由select实现

func addNumberToChan(chanName chan int) {for {chanName 

select监听多路io就是多个chan,没有chan有数据会走default代码块,顺序是随机轮询的没有default语句的话select也会继续循环轮询监听

range循环从chan读数据,就是遍历buf数组,当chan没有数据会阻塞到goroutine

func chanRange(chanName chan int) {    for e := range chanName {        fmt.Printf("Get element from chan: %d", e)     }}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang中,基本数据类型包括整型、浮点型、布尔型、字符串和字符型。引用数据类型包括数组、切片、映射、结构体和接口。 1. 整型:Golang提供了多种整型,包括intint8、int16、int32、int64、uint、uint8、uint16、uint32和uint64等。它们分别表示有符号和无符号的整数,不同类型的整数有不同的取值范围。 2. 浮点型:Golang提供了两种浮点型,即float32和float64。它们分别表示单精度和双精度浮点数,用于表示小数。 3. 布尔型:Golang的布尔型只有两个取值,即true和false。用于表示逻辑值。 4. 字符串:Golang中的字符串是一系列字节的集合,使用双引号或反引号括起来。字符串是不可变的,可以通过索引访问其中的字符。 5. 字符型:Golang中的字符类型使用单引号括起来,表示一个Unicode字符。 引用数据类型: 1. 数组:数组是一组具有相同类型的元素的集合,长度固定。可以通过索引访问数组中的元素。 2. 切片:切片是对数组的抽象,它可以动态增长和缩小。切片是一个引用类型,可以通过切片操作符[:]来获取子切片。 3. 映射:映射是一种无序的键值对集合,也称为字典或哈希表。可以通过键来访问对应的值。 4. 结构体:结构体是一种自定义的数据类型,可以包含多个字段,每个字段可以有不同的类型。结构体用于组织和存储相关的数据。 5. 接口:接口是一种抽象类型,定义了一组方法的集合。接口可以被其他类型实现实现了接口的类型可以被当作该接口类型使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值