golang channel的使用技巧

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

go的横空出世,让很多人眼前一亮,它的语法以简洁著称,并且它对多核并发的原生支持,让他在云计算和分布式领域展露头脚,它的核心围绕channel和goroutine展开。

首先golang channel 分为有缓冲与无缓冲两种类型,很多人认为无缓冲channel单单只是 默认缓冲为1缓冲的channel,其实它们最大的区别是阻塞问题。如下

c1:=make(chan int)        无缓冲  

c2:=make(chan int,1)      有缓冲

c1<-1                            

在c1<- 1处,因为c1是无缓冲的,因此程序会阻塞在这里,不会往下执行,但是c2则不会,他在channel满之前是不会阻塞的,他会继续往下执行,但是如果你在c2第一个数据未取出之前继续塞第二个数据,就会阻塞在你放第二个数据处,因为她的缓冲区单位只有1.例:

dgucochann := make(chan int,2)   

dgucochann <- 1

fmt.Println("Hello DGuco")

dgucochann <- 2    

fmt.Println("Hello Yuki")  

dgucochann <- 3  

fmt.Println("Hello Go")

在这种情况下,程序在第6行处会阻塞,会打印前两句话Hello DGuco和Hello Yuki,因为该channel只有两个单位的缓冲,在第6行时没有另外一个goroutine去取出里面的数据,所以阻塞在这里。

下面我们来介绍一下channel的基本用法。

1 信号量的传递

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
           
package main

import (
"fmt"
"time"
)

var mychan = make ( chan int )

func dgucofunc1 () {
fmt . Println ( "In dguco_func1" )
mychan <- 1
}

func dgucofunc2 () {
a := <- mychan
fmt . Println ( "In dguco_func1" )
}
func main () {
go dgucofunc1 ()
go dgucofunc2 ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucosignal.go


可以看到依次打出了相应的两段文字,如果把第6行去掉,因为在dgucofunc2收不到来自channel的信号就会阻塞,结果只会打印In dguco_func1
2 生产者消费者问题
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
            
package main

import (
"fmt"
"time"
)

func dgucoproducer ( c chan int , max int ) {
for i := 0 ; i < max ; i ++ {
c <- i
}
}

func dgucoconsumer ( c chan int ) {
ok := true
for ok {
if value , ok := <- c ; ok {
fmt . Println ( value )
}
}
}

func main () {
c := make ( chan int )
defer close ( c )
go dgucoproducer ( c , 10 )
go dgucoconsumer ( c )
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucocustomer.go
这个很简单,执行结果依次打印0-9
3  定时器(这个经常用)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
             
package main

import "time"
import "fmt"

var mychannel = make ( chan bool )

func dgucotimertask () {
fmt . Println ( "My TimerTask.." )
}

func dgucotimer () {
//返回一个定时器
mytimer := time . NewTicker ( time . Millisecond * 1000 )
select {
case <- mytimer . C :
go dgucotimertask ()
}
}

func main () {
dgucotimer ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucotimer.go
顺便说一下select的作用,首先它就相当于switch,但是他是用于channel的,举个例子:

select{
case <- c1:
.........
case <- c2:
........:
当程序执行到这里时,程序会阻塞在此,直到c1或者c2中有数据被放入就会继续执行,当然一般不会这样写,因为如果c1,和c2永远收不到数据程序阻塞在这里肯定不是我们想要的结果,一般我们都会写一个超时处理。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
            
package main

import "time"
import "fmt"

var mychannel = make ( chan bool )

func dgucotimertask () {
fmt . Println ( "My TimerTask.." )
}

func dgucotimer () {
timeout := time . NewTicker ( time . Millisecond * 10000 )

select {
case <- mychannel :
go dgucotimertask ()
case <- timeout . C :
fmt . Println ( "Time out" )
}
fmt . Println ( "Hello go" )
}

func main () {
dgucotimer ()
time . Sleep ( time . Millisecond * 10 )
}
 来自CODE的代码片
dgucotimeout.go
这里我们是没有向mychannel中写数据的,但是等到10秒之后会打印出hello go,程序并没有因此阻塞在这里。

4 goroutine 通信
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
            
package main

import "time"
import "fmt"

var mychannel = make ( chan int )

func dgucoroutine () {
for i := 0 ; i < 10 ; i ++ {
mychannel <- i
}

}
func dgucoroutine1 () {
for {
i := <- mychannel
fmt . Println ( "dgucoroutine1:" , i )
}
}

func dgucoroutine2 () {
for {
i := <- mychannel
fmt . Println ( "dgucoroutine2:" , i )
}
}

func main () {
go dgucoroutine ()
go dgucoroutine1 ()
go dgucoroutine2 ()
// mychannel <- 4
time . Sleep ( time . Millisecond * 100 )
}
 来自CODE的代码片
dgucodemo.go

这里实现了一个goroutine写,两个goroutine读的过程,可以看到按顺序写进去的数据,被哪个goroutine读到是完全随机的,在golang中我们要实现进程间通信,channel是唯一途径,也是推荐的途径,她的底层是通过共享内存实现的,速度非常快,这样我们摆脱了linux系统繁琐,古老的进程间通信方式,管道,消息队列,信号量等,非常的方便。
最后做一个总结,在使用channel的时候,一定要至少有一个goroutine来负责读(除非你有特殊的需求),否则你的程序就会阻塞在你写channel的地方。
GolangChannel详细介绍 缓冲通道在通道里无法存储数据,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,通道中无法存储数据。当一个通道被关闭后,再往该通道发送值会引发panic,从该通道取值的操作会先取完通道中的值。此时的通道称之为无缓冲通道。由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。一般在通道的声明时,都不会刻意声明为单通道,这样做会声明一个只进不出,或者只出不进的单通道,没有任何意义。 阅读详情

相关推荐

详解Golangchannel的用法

golang并发编程实践中,channel的正确运用直接影响程序的健壮性和执行效率。本文将深入探讨几种提升channel使用效能的典型场景与实现策略。

5884

golangchannel 全面解析

golang通道的详细解读

Hud的博客 9043

Golang Channel 详细原理和使用技巧

Golang Channel 详细原理和使用技巧,channel使用原理以及使用方式和注意事项

资深系统架构,深耕高并发研发与 AI 大模型工程化落地。技术栈以 Python (AI/NLP/RAG/Agent) 与 Golang (高并发架构) 为双核心,兼具 PHP 大型应用经验。专注算法落地、复杂系统设计及团队技术赋能 3406

golang——channel

分类 channel的三种状态 channel的两种类型——有缓冲,无缓冲缓冲缓冲 2.操作 1.创建 2.发送 3.接收 4.关闭 3.使用场景 4.channel底层 5.channel线程安全 为什么是线程安全的 如何实现线程安全 6.channel控制goroutine并发执行顺序 7.channel共享内存的优缺点 8.channel发送和接收什么时候会死锁

weixin_45627369的博客 4167

Golang Channel 实战技巧和说明

Golang Channel 实战技巧和说明

后端系统和架构 853

Go 语言channel的应用场景及使用技巧

Go 语言channel的应用场景及使用技巧

禅与代码的艺术 1456

聊聊go语言channel中的一些小技巧

对应的我们给出这样一个select实现非阻塞轮询的例子,子协程非阻塞轮询所有channel,发现channel都不具备处理的条件,于是将该协程存到各个channel的阻塞队列中。因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注。因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注。逻辑发现数据无法投递时,则走到下一个分支查看是否可以执行,结果有缓冲区。

禅与计算机程序设计的艺术 643

Go Channel 死锁问题定位技巧

例如,主协程提前结束,子协程尚未完成Channel操作,程序便会阻塞。使用`sync.WaitGroup`或`context.Context`管理协程生命周期,确保所有协程完成任务后再退出,能有效避免这类问题。在Go语言中,Channel是协程间通信的核心机制,但使用不当容易引发死锁问题。例如,协程A等待协程B的Channel消息,而协程B又在等待协程A的消息,形成死锁。例如,`go run -race`可检测数据竞争,而`pprof`能分析协程阻塞情况。**1. 检查发送与接收的匹配**

qznquf_845的博客 246

23、掌握Go并发编程:goroutine与channel的实战技巧

本文深入探讨了Go语言的并发模型,重点介绍了goroutine和channel使用。我们了解了goroutine的轻量级线程特性和channel在goroutine间传递数据的机制。通过实战案例,我们掌握了在Go中实现并发下载任务和生产者-消费者模式的方法。最后,我们总结了Go并发编程的优势,并强调了在实际应用中选择合适并发模式的重要性。

silenceallat的博客 1128

go get 代理_Go语言 | 从并发模式看channel使用技巧

最近重看MinIO的源代码,发现纠删码模式下读取数据盘的时候,使用了更简单的并发读取方式,以前看的时候没发现,查了下Git历史记录,发现是19年新改的,新的使用channel做标记的方式的确非常巧妙,简化了代码逻辑,值得我们学习。所以今天就开篇文章,介绍下channel在并发下的两个使用技巧。赢者为王模式这种并发模式并不稀奇,相信很多朋友都用到过。它的核心思想就是同时开几个协程做同样的事...

weixin_29001419的博客 146

Golang channel的异常处理技巧

Golang的并发编程中,channel就像是一个神奇的管道,让不同的程序部分能够顺畅地交流数据。但在使用这个管道的过程中,可能会遇到各种问题,比如管道堵塞、发送数据到已关闭的管道等。本文的目的就是教大家如何巧妙地处理这些问题,让我们的并发程序更加稳定可靠。我们会涵盖channel异常处理的各个方面,从基本概念到实际应用,让大家对channel的异常处理有一个全面的了解。本文将按照以下结构进行介绍:首先会解释channel相关的核心概念,让大家明白channel是什么,以及它的发送和接收操作是怎么回事。

AI Agent Harness Engineering 635

Golangchannel使用以及并发同步技巧

在学习《The Go Programming Language》第八章并发单元的时候还是遭遇了不少问题,和值得总结思考和记录的地方。 做一个类似于unix du命令的工具。但是阉割了一些功能,这里应该只实现-c(统计total大小) 和-h(以human比较容易辨识的显示出来)的功能。 首先我们需要构造一个 能够返回FileInfo信息数组的函数,我们把它取名为dirEntries: ...

weixin_30663471的博客 101

Go Channel 死锁检测实战技巧

在并发编程中,Go语言的Channel是协程间通信的重要工具,但使用不当容易引发死锁问题。本文将分享几个实战技巧,帮助开发者快速检测和解决Channel死锁问题,提升代码的健壮性。通过Add、Done和Wait方法,确保所有协程执行完毕后再结束程序,从而减少因协程未启动而引发的死锁。例如,在从Channel读取数据时设置超时,若超过指定时间未成功,则触发超时逻辑并释放资源,防止死锁僵局。实际开发中,建议结合日志和工具链,形成系统的死锁防范机制,从而提升并发程序的稳定性。**1. 理解死锁成因**

xmekau_478的博客 188
上一篇: Golang的坑之http读取大文件必须读完
下一篇: 【Go语言】连接数据库SQLite、MySQL、Oracle - Mike_zh
QQ7650371
博客等级 码龄11年 160粉丝 192原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值