热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

16.2Go语言干货并发编程之channel

1.channel1.1函数与函数之间如何进行通讯?1.通过共享内存实现函数之间的通讯共享内存实现函数间的通讯有个极大的弊端,不同的gorouti

1.channel


1.1函数与函数之间如何进行通讯?

1.通过共享内存实现函数之间的通讯

共享内存实现函数间的通讯有个极大的弊端,不同的`goroutine`容易发生竞态问题(数据不安全),未来保证数据交换的正确性,必须要给数据进行加锁,这样就会造成性能问题。

2.通过通信共享内存

在Go语言中就是利用`channel` 实现两个`goroutine`之间通过通信共享内存

在Go语言中goroutine是并发的执行体,channel就是执行体之间的连接。
channel可以让一个goroutine发送特定的值到另外一个goroutine的通讯机制。

Go语言中的通道channel是一种特殊的类型,我们可以把这个通道看做成队列,遵循先进先出的规则。

这里要特别说明:每个管道都是一个具体类型的导管,在声明channel的时候需要为其指定元素的类型

1.2 channel类型

channel是一种引用类型,(必要要进行make函数初始化后才能使用)

channel声明格式

var 变量 chan 元素类型

举几个栗子

var cha1 chan int64 //声明一个传递64位整型的通道
var cha2 chan bool //声明一个传递布尔的通道
var cha3 chan []int //声明一个传递int切片的通道

1.3 创建channel

chanel通道是一个引用类型,空值为nil

var cha1 chan int
fmt.Println(cha) //nil

** 声明通道后,必须使用make函数初始化后才能使用**

make(chan 元素类型,缓冲大小) //缓冲大小是可选的

举几个栗子

cha1 := make(chan int)
cha2 := make(chan bool)
cha3 := make(chan []int)

1.4 channel的操作

发送(send)、接收(receive)、关闭(close)
发送与接收使用的唯一符号<-

我们先定义一个发送int类型的通道

cha1 :&#61; make(chan int)

发送&#xff1a;将一个值发送到通道中

cha1 <- 10 // 把10发送到cha1通道中

接收&#xff1a;变量接收通道的值

n :&#61; <- cha1 // 变量n接收通道cha1中的一个值
<- cha1 // 抛弃cha1中的一个值

关闭&#xff1a;通过内置函数close()来关闭通道

close(cha1)

关闭通道需要注意&#xff0c;只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的&#xff0c;它和关闭文件是不一样的&#xff0c;在结束操作之后关闭文件是必须要做的&#xff0c;但关闭通道不是必须的。

关闭后的通道有以下特点&#xff1a;
1.对一个已经关闭的通道发送值会导致panic
2.对一个已经关闭的通道进行接收会一直获取通道里面的值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。

1.5 无缓冲的通道

无缓冲的通道又名阻塞通道

为什么叫做阻塞通道呢&#xff1f;

package mainvar cha1 &#61; make(chan int64)
func main(){cha1 <- 10
}

编译通道在执行的时候报错

atal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
main.main()G:/go/go_pro/src/github.com/day08/channel/main.go:5 &#43;0x45
exit status 2

这就是无缓冲通道又叫做阻塞通道的原因
我们创建了一个无缓冲通道var cha1 &#61; make(chan int64),无缓冲通道&#xff08;阻塞通道&#xff09;只有在有人接收值的时候才能发送值
举个通俗一点的栗子
无缓冲通道就像你住的小区没有快递柜与代收点&#xff0c;快递员必须将这个东西送到你的手里。
无缓冲通道必须有接收值才能发送

package mainimport ("fmt""sync"
)var cha1 &#61; make(chan int64)
var wg sync.WaitGroupfunc recv1(c chan int64) {defer wg.Done()n :&#61; <-cfmt.Println("接收成功", n)
}
func main() {wg.Add(1)go recv1(cha1)cha1 <- 10fmt.Println("已经将值发送给通道")wg.Wait()
}

1.6 有缓冲通道

举个栗子

package main
func main() {cha1 :&#61; make(chan int64, 1)cha1 <- 10
}

没有报错

1.7 for range从通道中循环取值

当向通道发送完数据后&#xff0c;使用close()关闭通道&#xff0c;使用for range对通道进行循环取值。

当通道被关闭时&#xff0c;在向通道中进行发送值会引发panic,从通道取值的操作会先取完通道中的值&#xff0c;然后取到的是对应类型的零值。

举个栗子

// 开启一个goroutine&#xff0c;将0-100发送给cha1
// 再开启一个goroutine,将cha1中的值平方后发给cha2
// 再开启一个goroutine,在cha2中取值并打印
package mainimport ("fmt""sync"// "time"
)var wg sync.WaitGroupfunc main() {cha1 :&#61; make(chan int)cha2 :&#61; make(chan int)// 开启一个goroutine&#xff0c;将0-100发送给cha1go func() {defer wg.Done()for i :&#61; 0; i < 100; i&#43;&#43; {cha1 <- i}close(cha1)}()// 开启一个goroutine&#xff0c;在cha1中取值并且平方后发送给cha2go func() {defer wg.Done()for {i, ok :&#61; <-cha1if !ok {break}cha2 <- i * i}close(cha2)}()go func() {defer wg.Done()for i :&#61; range cha2 {fmt.Println(i)}}()wg.Add(3)fmt.Println("main函数")wg.Wait()
}

从上面的例子中我们看到有两种方式在接收值的时候判断该通道是否被关闭&#xff0c;不过我们通常使用的是for range的方式。使用for range遍历通道&#xff0c;当通道被关闭的时候就会退出for range。

1.8 单向通道

有的时候我们会将通道作为参数在多个任务函数间传递&#xff0c;很多时候我们在不同的任务函数中使用通道都会对其进行限制。

比如限制通道在函数中只能发送或只能接收。

Go语言中提供了单向通道来处理这种情况&#xff1a;

func counter(out chan<- int) {for i :&#61; 0; i < 100; i&#43;&#43; {out <- i}close(out)
}func squarer(out chan<- int, in <-chan int) {for i :&#61; range in {out <- i * i}close(out)
}
func printer(in <-chan int) {for i :&#61; range in {fmt.Println(i)}
}func main() {ch1 :&#61; make(chan int)ch2 :&#61; make(chan int)go counter(ch1)go squarer(ch2, ch1)printer(ch2)
}

其中&#xff0c;
1.chan<- int是一个只写单向通道&#xff08;只能对其写入int类型值&#xff09;&#xff0c;可以对其执行发送操作但是不能执行接收操作&#xff1b;
2.<-chan int是一个只读单向通道&#xff08;只能从其读取int类型值&#xff09;&#xff0c;可以对其执行接收操作但是不能执行发送操作。
在函数传参及任何赋值操作中可以将双向通道转换为单向通道&#xff0c;但反过来是不可以的。

2. 通道总结

在这里插入图片描述
关闭已经关闭的channel也会引发panic


推荐阅读
  • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
author-avatar
菜鸟一号
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有