前言
Hi,大家好,我是码农,星期八,本篇继续带来Go语言并发基础,channel如何使用。
看看Go协程如何配合channel。
为什么需要channel
channel在Go中,也叫做管道,是用来多线程之间共享数据的。
通常情况下,在Go中共享数据用的也是channel,但是在Go有两种共享数据方式。
为啥子共享内存通讯不太推荐?
示例代码:多线程修改一个值。
函数
- 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)
- }
执行结果
没错,是2,懵了吧,哈哈哈,理论应该是0才对呀。
这是为啥?
这就是共享内存不太推荐的原因,我们的代码已经是多线程了。
在第一个函数代码中,第3行,NUM = NUM - 1。
如果多个线程同时执行到这一行,并且没有加锁,就会出现数据错乱。
那该怎么做呢?
加锁,加锁可以保证某一段代码只能被一个线程执行,防止被争抢。
代码
- func Calc() {
- defer wg.Done()
- mutex.Lock()
- NUM = NUM - 1
- mutex.Unlock()
- }
第3行加锁,第5行解锁。
执行结果
这次真的是0的,不管执行几次。
但是会发现一个问题,如果采用这种方式,需要常常注意竞争问题。
所以不是太推荐,需要考虑的比较多,并且各种加锁会消耗性能。
channel语法
channel格式
- var 变量名 chan 类型
- 例如
- var x1 chan int //x1管道里面只能存int类型数据
- var x2 chan string //x2管道里面只能存字符串类型数据
注意
定义管道时,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是先入先出结构,就像这样。
注意事项:
无缓冲管道
无缓冲就是这个管道没有长度,就像这样。
就像快读员没有快递柜,需要直接将快递给客户,如果没人要就撂摊子。
示例代码
- 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行代码。
直接报错,all goroutines are asleep - deadlock!,这句话的意思是所有的协程都睡着了,死锁
无缓冲说明通道长度为0,发送一个值会阻塞住。
这就相当于快递员直接找张三,但是张三没了,但是快递员还得一直等着,等等等,然后挂了,终究还是没送出去。
有缓冲管道
这个就简单啦,多了一个快递柜,快递员直接将快递仍快递柜就行了。
示例代码
- 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()
- }
执行结果
遍历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)
- }
- }
执行结果
报错是因为我在main中完成了发送值和取值两个操作,所以会出现上述问题,但是结果是没有错的。
单向通道
我们知道通道是可以发送值和取值的,但是某些场景为了安全起见,理论来说只能取值,后者只能发送值。
单向通道通常只在函数参数中体现。
修改上述快递员代码。
- 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配合使用,毕竟我们一般的任务都不是单独运行的,都是互相配合的。
我们讲述了如何创建channel,如何使用channel,有缓冲管道和无缓冲管道区别,并且拒了一个快递员例子来展示协程和channel如何配合,最后用单向通道又加固了一下代码。
我的代码中使用了中文命名变量名是为了好看,实际开发中千万不要这样!!!
上述代码一定要敲一下,如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。
不积跬步无以至千里,不积小流无以成江海,给自己一个成长的时间
分享题目:一篇文章带你了解Go语言基础之并发(channel)
分享URL:http://www.shufengxianlan.com/qtweb/news16/328916.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联