前言大家好,我是码农。周五,本文继续带来Go语言并发的基础知识以及通道的使用方法。查看Go协程如何与通道一起工作。为什么需要channel在Go中,channel,也叫管道,用于多个线程之间共享数据。通常情况下,Go中也使用Channel来共享数据,但是Go中有两种共享数据的方式。用于通信的共享内存。通过渠道沟通(推荐)。为什么不推荐共享内存通信?示例代码:多线程修改一个值。函数funcCalc(){deferwg.Done()NUM=NUM-1}mainvarNUM=100varwgsync.WaitGroupfuncmain(){fori:=0;i<100;i++{wg.Add(1)goCalc()}wg.Wait()fmt.Println(NUM)}的执行结果是正确的,是2,我糊涂了,哈哈哈,理论上应该是0,这是为什么呢?这就是不推荐共享内存的原因,我们的代码已经是多线程的了。第一个函数代码,第3行,NUM=NUM??-1,如果多个线程同时执行到这一行,而且没有加锁,就会出现数据损坏。那怎么办呢?加锁,加锁可以保证某一段代码只能由一个线程执行,防止被乱码。代码funcCalc(){deferwg.Done()mutex.Lock()NUM=NUM-1mutex.Unlock()}第3行锁定,第5行解锁。这次执行结果真的是0,不管执行多少次。但是你会发现一个问题。如果使用这种方法,需要注意竞争问题。所以不是很推荐,考虑的东西比较多,各种锁会消耗性能。channel语法通道格式var变量名chan类型如varx1chanint//x1管道只能存储int类型数据varx2chanstring//x2管道只能存储string类型数据注意定义管道时chanint是一个整体,不要make每个人都错了。Createachannel创建频道,只能通过make创建。formatvarvariablename=make(chantype,[pipelinesize])examplevarchan1=make(chanint,10)//管道可以容纳10个int元素varchan2=make(chanstring,5)//管道可以容纳5个字符串元素通道操作创建一个管道。ch=make(chanint,10)channel是一个通道,就像一个管道。所以这就像把东西塞进管子里,你可以把东西拿出来。关闭管道意味着管道不能再被使用,里面的值就证明了。像pipe(send)ch<-666这样的东西。从pipe(receive)varx=<-ch中获取一些东西。关闭管道close(ch)。注意:通道是先进先出的结构,像这样。注意:如果通道已满,重新插入会卡住。如果channel关闭了,就不能再塞值了,否则会panic。即使关闭了通道,仍然可以取到值,直到取到管道的值,取完后得到对应的类型零值。管道不能重复关闭,重复关闭会死机。UnbufferedpipeUnbuffered是没有长度的管道,像这样。就好比快阅读没有快递柜,需要直接把快递送到客户手上,没人要就走人。示例代码packagemainimport("fmt")//模拟张三funcZhangsan(xchanstring){vara=<-xfmt.Println(a)}funcmain(){//通道没有长度,是一个无缓冲的通道varx=make(chanstring)go张三(x)x<-"张三的快递"fmt.Println("张三的快递发货成功")}在第16行写入一个值,同理,张三会等待取件,如果If没人接,结束了。假设代码的第9行被注释掉了。直接报错,allgoroutinesaresleep-deadlock!,这句话的意思是所有goroutinesaresleeve,deadlockwithoutbuffer表示channel长度为0,发送一个值会阻塞。这就相当于快递员直接找张三了,张三不见了,快递员还得等啊等,挂了,终究还是没有送达。有一个缓冲管道,很简单。多设一个快递柜,快递员可直接将快件退回快递柜。示例代码packagemainimport("fmt""sync")varwgsync.WaitGroup//快递员,快递员放10个快递员funccourier(kuaidiguichanstring){deferwg.Done()fori:=0;i<10;i++{fmt.println("Thecourierputinthefirst",i,"thecourier")kuaidigui<-fmt.Sprintf("The%dcourier",i)}//快递送达后关闭通道close(kuaidigui)}//张三,带走3个快递funcZhangSan(kuaidiguichanstring){deferwg.Done()fori:=0;i<3;i++{fmt.Println("张三带走"+<-kuaidigui)}}//李四带走了7个快递func李四(kuaidiguichanstring){deferwg.Done()fori:=0;i<7;i++{fmt.Println("李四带走了"+<-kuaidigui)}}funcmain(){//快递柜,10个尺寸varexpresscabinet=make(chanstring,10)wg.Add(3)gocourier(快递柜)go张三(快递柜)golisi(快递柜)wg.Wait()}执行结果通过两种方式遍历通道代码funcmain(){//快递柜,10个大小varch=make(chanint,10)//将值送入管道fori:=0;i<10;i++{ch<-i}//方法1取值//for{//i,ok:=<-ch////Ok取值后为false//if!ok{////结束循环//break//}//fmt.Println(i)////第二种获取fori值的方法:=rangech{fmt.Println(i)}}执行结果报错是因为我在mainOperation中已经完成了发送和取值,所以会出现上面的问题,但是结果并没有错。单向通道我们知道通道可以发送和获取值,但是在某些场景下,为了安全起见,理论上只能获取值,后者只能发送值。单向通道通常只体现在功能参数上。形参chan<-chan类型是只写的。形参<-chanchan类型是只读的。修改上面的快递代码。packagemainimport("fmt""sync")varwgsync.WaitGroup//快递员,快递员放10个快递员,只写chan<-stringfunccourier(kuaidiguichan<-string){deferwg.Done()fori:=0;i<10;i++{fmt.Println("Thecourierputinthefirst",i,"thecourier")kuaidigui<-fmt.Sprintf("The%dcourier",i)}//快递通道关闭后关闭(kuaidigui)}//张三,带走3个快递,只读<-chanstringfunc张三(kuaidigui<-chanstring){deferwg.Done()fori:=0;i<3;i++{fmt.Println("张santakeaway"+<-kuaidigui)}}//李四拿了7个快递func李四(kuaidigui<-chanstring){deferwg.Done()fori:=0;i<7;i++{fmt.Println("李四拿走"+<-kuaidigui)}}funcmain(){//快递柜,10个尺寸var快递柜=make(chanstring,10)wg.Add(3)gocourier(快递柜)go张三(快递柜)go李四(快递柜)wg.Wait()}总结以上介绍了Go语言并发如何与通道结合使用。毕竟我们一般的任务都不是单独跑的,都是相互配合的。我们描述了如何创建通道,如何使用通道,缓冲管道和无缓冲管道之间的区别,并拒绝了一个快递员的例子来展示协程和通道如何协同工作,最后用一个-方式通道。我在我的代码中使用中文命名的变量名是为了好看。实际开发中不要这样做!!!必须输入上面的代码。如果您在操作过程中有任何问题,记得在下方留言。当我们看到它时,我们将是第一个。解决问题的时间。不积小步不成千里,不积小流不成江海。给自己成长的时间
