电子邀请函免费制作app网奇seo赚钱培训

当前位置: 首页 > news >正文

电子邀请函免费制作app,网奇seo赚钱培训,中英文网站切换,wordpress 总数 函数文章目录 1 概念2 分类3 操作3.1 channel 的创建3.1.1 无缓冲channel3.1.1 带缓冲channel 3.2 channel的读写3.3 channel的关闭3.4 channel 和 select 4 channel 底层原理 1 概念 channel 是一个通道#xff0c;用于端到端的数据传输#xff0c;这有点像我们平常使用的消息队… 文章目录 1 概念2 分类3 操作3.1 channel 的创建3.1.1 无缓冲channel3.1.1 带缓冲channel 3.2 channel的读写3.3 channel的关闭3.4 channel 和 select 4 channel 底层原理 1 概念 channel 是一个通道用于端到端的数据传输这有点像我们平常使用的消息队列只不过 channel 的发送方和接受方是 goroutine 对象属于内存级别的通信。 2 分类 3 操作 在深入了解 channel 的底层之前我们先来看看 channel 的常用用法。 3.1 channel 的创建 3.1.1 无缓冲channel ch : make(chan int)对于无缓冲的 channel一旦有 goroutine 往 channel 写入数据那么当前的 goroutine 会被阻塞住直到有其他的 goroutine 消费了 channel 里的数据才能继续运行写入数据。 3.1.1 带缓冲channel 还有另外一种是有缓冲的 channel它的创建是这样的: ch : make(chan int, 10)其中第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量则当前的 goroutine 就不会被阻塞住。 另外我们也可以声明一个 nil 的 channel只是创建这样的 channel 没有意义读、写 channel 都将会被阻塞住。一般 nil channel 用在 select 上让 select 不再从这个 channel 里读取数据如下用法 ch1 : make(chan int) ch2 : make(chan int) go func() {if !ok { // 某些原因设置 ch1 为 nilch1 nil}}() for {select {case -ch1: // 当 ch1 被设置为 nil 后将不会到达此分支了。doSomething1()case -ch2:doSomething2()} }3.2 channel的读写 3.3 channel的关闭 ch : make(chan int,10)… close(ch)面试题当关闭channel之和再操作channel会发生什么 写数据则程序会直接 panic 退出 读数据(1) 有数据读到关闭之前写入的数据     (2) 无数据将得到零值即对应类型的默认值。 3.4 channel 和 select 在写程序时有时并不单单只会和一个 goroutine 通信当我们要进行多 goroutine 通信时则会使用 select 写法来管理多个 channel 的通信数据 ch1 : make(chan struct{})ch2 : make(chan struct{})// ch1, ch2 发送数据go sendCh1(ch1)go sendCh1(ch2)// channel 数据接受处理for {select {case -ch1:doSomething1()case -ch2:doSomething2()}}channel 的死锁 前面提到过往 channel 里读写数据时是有可能被阻塞住的一旦被阻塞则需要其他的 goroutine 执行对应的读写操作才能解除阻塞状态。 然而阻塞后一直没能发生调度行为没有可用的 goroutine 可执行则会一直卡在这个地方程序就失去执行意义了。此时 Go 就会报 deadlock 错误如下代码 func main() {ch : make(chan int)-ch// 执行后将 panic// fatal error: all goroutines are asleep - deadlock!}因此在使用 channel 时要注意 goroutine 的一发一取避免 goroutine 永久阻塞 4 channel 底层原理 前面提及过 channel 创建后返回了 hchan 结构体现在我们来研究下这个结构体它的主要字段如下 type hchan struct {//channel分为无缓冲和有缓冲两种。//对于有缓冲的channel存储数据借助的是如下循环队列的结构qcount uint // 循环队列中的元素数量dataqsiz uint // 循环队列的长度buf unsafe.Pointer // 指向底层循环队列的指针elemsize uint16 //能够收发元素的大小closed uint32 //channel是否关闭的标志elemtype *_type //channel中的元素类型//有缓冲channel内的缓冲数组会被作为一个“环型”来使用。//当下标超过数组容量后会回到第一个位置所以需要有两个字段记录当前读和写的下标位置sendx uint // 下一次发送数据的下标位置recvx uint // 下一次读取数据的下标位置//当循环数组中没有数据时收到了接收请求那么接收数据的变量地址将会写入读等待队列//当循环数组中数据已满时收到了发送请求那么发送数据的变量地址将写入写等待队列recvq waitq // 读等待队列sendq waitq // 写等待队列lock mutex //互斥锁保证读写channel时不存在并发竞争问题 }channel 在进行读写数据时会根据无缓冲、有缓冲设置进行对应的阻塞唤起动作它们之间还是有区别的。下面我们来捋一下这些不同之处。 无缓冲 channel 由于对 channel 的读写先后顺序不同处理也会有所不同所以还得再进一步区分 channel 先写再读 在这里我们暂时认为有 2 个 goroutine 在使用 channel 通信按先写再读的顺序则具体流程如下 可以看到由于 channel 是无缓冲的所以 G1 暂时被挂在 sendq 队列里然后 G1 调用了 gopark 休眠了起来。 接着又有 goroutine 来 channel 读取数据了 此时 G2 发现 sendq 等待队列里有 goroutine 存在于是直接从 G1 copy 数据过来并且会对 G1 设置 goready 函数这样下次调度发生时 G1 就可以继续运行并且会从等待队列里移除掉。 channel 先读再写 先读再写的流程跟上面一样。 G1 暂时被挂在了 recvq 队列然后休眠起来。 G2 在写数据时发现 recvq 队列有 goroutine 存在于是直接将数据发送给 G1。同时设置 G1 goready 函数等待下次调度运行。 有缓冲 channel 在分析完了无缓冲 channel 的读写后我们继续看看有缓冲 channel 的读写。同样的我们分为 2 种情况 channel 先写再读 这一次会优先判断缓冲数据区域是否已满如果未满则将数据保存在缓冲数据区域即环形队列里。如果已满则和之前的流程是一样的。 当 G2 要读取数据时会优先从缓冲数据区域去读取并且在读取完后会检查 sendq 队列如果 goroutine 有等待队列则会将它上面的 data 补充到缓冲数据区域并且也对其设置 goready 函数。 channel 先读再写 此种情况和无缓冲的先读再写是一样流程此处不再重复说明。 四、总结 有缓冲 channel 和无缓冲 channel 的读写基本相差不大只是多了缓冲数据区域的判断而已。 channel 在使用的时候大多时候得和 select 配合使用尽管只需要简单的用 - ch 和 ch - 来读写数据但它的底层还是很有讲究的特别是涉及到调度的休眠唤起。 这也能看出 Go 的精妙之处复杂底层优雅运用。