温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Go语言基础并发channel

发布时间:2021-09-03 11:50:07 来源:亿速云 阅读:174 作者:chen 栏目:web开发

这篇文章主要讲解了“Go语言基础并发channel”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言基础并发channel”吧!

为什么需要channel

channel在Go中,也叫做管道,是用来多线程之间共享数据的。

通常情况下,在Go中共享数据用的也是channel,但是在Go有两种共享数据方式。

  • 共享内存实现通讯。

  • 通过管道(channel)通讯(推荐)。

为啥子共享内存通讯不太推荐?

示例代码:多线程修改一个值。

函数

func Calc() {     defer wg.Done()     NUM = NUM - 1 }

main

var NUM = 100 var wg sync.WaitGroup  func main() {     for i := 0; i<100;i++  {         wg.Add(1)         go Calc() }     wg.Wait()     fmt.Println(NUM) }

执行结果

Go语言基础并发channel

没错,是2,懵了吧,哈哈哈,理论应该是0才对呀。

这是为啥?

这就是共享内存不太推荐的原因,我们的代码已经是多线程了。

在第一个函数代码中,第3行,NUM = NUM - 1。

如果多个线程同时执行到这一行,并且没有加锁,就会出现数据错乱。

那该怎么做呢?

加锁,加锁可以保证某一段代码只能被一个线程执行,防止被争抢。

代码

func Calc() {     defer wg.Done()     mutex.Lock()     NUM = NUM - 1     mutex.Unlock() }

第3行加锁,第5行解锁。

执行结果

Go语言基础并发channel

这次真的是0的,不管执行几次。

但是会发现一个问题,如果采用这种方式,需要常常注意竞争问题。

所以不是太推荐,需要考虑的比较多,并且各种加锁会消耗性能。

channel语法

channel格式

var 变量名 chan 类型 例如 var x1 chan int //x1管道里面只能存int类型数据 var x2 chan string //x2管道里面只能存字符串类型数据

注意

Go语言基础并发channel

定义管道时,chan int是一个整体,别搞错了各位。

创建channel

创建channel,只能通过make创建。

格式

var 变量名 = make(chan 类型,[管道大小]) 示例 var chan1 = make(chan int,10)//管道可以放10个int元素 var chan2 = make(chan string,5)//管道可以放5个string元素

channel操作

创建一个管道。

ch = make(chan int,10)

channel是一个管道,就像一个管子。

所以可以像管子里面塞东西,并且能取东西,关闭管道就是这个管道不能用了,里面的值取完就打样了。

像管子塞东西(发送)ch <- 666。

从管子取东西(接收)var x = <- ch。

关闭管子close(ch)。

注意:channel是先入先出结构,就像这样。

Go语言基础并发channel 

注意事项:

  • 如果通道塞满了,再塞 会阻塞住。

  • 如果通道关闭了,是不能再塞值了,否则会panic。

  • 即使通道关闭了,依然可以取值,直到将管道的值取完,取完后得到的是对应类型零值。

  • 管道不能重复关闭,重复关闭会panic。

无缓冲管道

无缓冲就是这个管道没有长度,就像这样。

就像快读员没有快递柜,需要直接将快递给客户,如果没人要就撂摊子。

Go语言基础并发channel

示例代码

package main  import (     "fmt" )  //模拟张三 func 张三(x chan string) {     var a = <-x     fmt.Println(a) }  func main() {     //通道没有长度,就是无缓冲通道     var x = make(chan string)     go 张三(x)     x <- "张三的快递"     fmt.Println("张三快递交付成功") }

第16行写入一个值,同理,张三就要等着去接,如果没人接,那就完了。

假设注释第9行代码。

Go语言基础并发channel

直接报错,all goroutines are asleep - deadlock!,这句话的意思是所有的协程都睡着了,死锁

无缓冲说明通道长度为0,发送一个值会阻塞住。

这就相当于快递员直接找张三,但是张三没了,但是快递员还得一直等着,等等等,然后挂了,终究还是没送出去。

有缓冲管道

Go语言基础并发channel

这个就简单啦,多了一个快递柜,快递员直接将快递仍快递柜就行了。

示例代码

package main  import (     "fmt"     "sync" )  var wg sync.WaitGroup  //快递员,快递员放10个快递 func 快递员(kuaidigui chan string) {     defer wg.Done()     for i := 0; i < 10; i++ {         fmt.Println("快递员放入了第",i,"快递")         kuaidigui <- fmt.Sprintf("第%d个快递", i) }     //放完快递就关闭了通道     close(kuaidigui) }  //张三,拿走3个快递 func 张三(kuaidigui chan string) {     defer wg.Done()     for i := 0; i < 3; i++ {         fmt.Println("张三拿走" + <-kuaidigui) } } //李四拿走7个快递 func 李四(kuaidigui chan string) {     defer wg.Done()     for i := 0; i < 7; i++ {         fmt.Println("李四拿走" + <-kuaidigui) } } func main() {     //快递柜,10个大小     var 快递柜 = make(chan string, 10)     wg.Add(3)     go 快递员(快递柜)     go 张三(快递柜)     go 李四(快递柜)     wg.Wait() }

执行结果

Go语言基础并发channel 

遍历channel两种方式

代码

func main() {     //快递柜,10个大小     var ch = make(chan int, 10)     //向管道中发送值     for i := 0; i < 10; i++ {         ch <- i }     //方式一取值     //for {     //i, ok := <-ch     ////取完值ok就是false     //if !ok {     //      //结束循环     //      break     //}     //fmt.Println(i)     //}     //方式二取值     for i:=range ch{         fmt.Println(i) } }

执行结果

Go语言基础并发channel

报错是因为我在main中完成了发送值和取值两个操作,所以会出现上述问题,但是结果是没有错的。

单向通道

我们知道通道是可以发送值和取值的,但是某些场景为了安全起见,理论来说只能取值,后者只能发送值。

单向通道通常只在函数参数中体现。

  • 形参 chan<- chan类型只写。

  • 形参 <-chan chan类型只读。

修改上述快递员代码。

package main  import (     "fmt"     "sync" )  var wg sync.WaitGroup  //快递员,快递员放10个快递,只写 chan<- string func 快递员(kuaidigui chan<- string) {     defer wg.Done()     for i := 0; i < 10; i++ {         fmt.Println("快递员放入了第", i, "快递")         kuaidigui <- fmt.Sprintf("第%d个快递", i) }     //放完快递就关闭了通道     close(kuaidigui) }  //张三,拿走3个快递,只读<-chan string func 张三(kuaidigui <-chan string) {     defer wg.Done()     for i := 0; i < 3; i++ {         fmt.Println("张三拿走" + <-kuaidigui) } }  //李四拿走7个快递 func 李四(kuaidigui <-chan string) {     defer wg.Done()     for i := 0; i < 7; i++ {         fmt.Println("李四拿走" + <-kuaidigui) } } func main() {     //快递柜,10个大小     var 快递柜 = make(chan string, 10)     wg.Add(3)     go 快递员(快递柜)     go 张三(快递柜)     go 李四(快递柜)     wg.Wait() }

总结

上述讲述了Go语言并发如何和channel配合使用,毕竟我们一般的任务都不是单独运行的,都是互相配合的。

感谢各位的阅读,以上就是“Go语言基础并发channel”的内容了,经过本文的学习后,相信大家对Go语言基础并发channel这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI