go语言并发编程上
傍晚抽空学习了下go语言的并发编程,从goroutine到channel机制,从开始的稀里糊涂到现在拨开云雾见太阳的感觉,学习的过程总是令人亢奋的!当然目前的理解还是不够透彻的。下篇将举例来分析下。
goroutine
类似开辟进程、线程做法,go语言所采用的为 goroutine 。用法极其简单,也就是使用go关键字,使用方法有两种:
-
定义一个函数functionName,要异步调用时使用语句go functionName即可。
-
使用匿名函数,用法为go func(参数列表){函数执行体}(),说明最后一个()作用就是让该函数执行。
下面简单代码加深下:
/////////第一种示例代码://///////// func sayHello(name string){ fmt.Println("hello"+name) } //主程序入口 func main(){ go sayHello("PMST") } /////////第二种示例代码:////////// //主程序入口 func main(){ go func(){ fmt.Println("hello world") }() //别忘记这里的() }
现在来讲下go关键字作用,一旦将go放在函数之前,意味分配一个子routine让这个函数自个玩去(有点自身自灭的感觉),而主 routine则继续该干嘛干嘛。打个比方,手头有一堆数据,需要经过超级复杂,超级耗时的计算才能得出答案。显然我们的主routine不可能只干这一 件事,它还有其他事情需要处理。因此,我们开辟一个routine让其自个算去。
那么问题来了,这么做确实解决了主routine不被锁死的问题,又能够完成了耗时计算,可是计算出答案之后,如何回传给主routine使用呢?
这也就是下面channel的使用了
channel
goroutine 之间如何进行数据的通信?如下两种:
-
共用内存内存空间。
-
Go语言推荐的通信机制channel。
接下来是我对channel的一些理解:
-
Goroutine使用channel 接收或者发送值,而这些值只能是特定的类型,由自己指定,比如两个routine之间相互传值为int类型,那么channel类型就是chan int
-
通过make来创建channel,例如无缓存ci := make(chan int),指定缓存cib := make(chan int,2)。很明显可以看出,区别在于后面的表述,后者表示给这个通道分类了2个缓存空间,至于区别,下文马上给出。
-
channel 通过<- 来接收和发送数据。例如 channel <- value ,显然就是把值 value 发送到通道 channel 中; value := <- ch ,注意箭头,表示从通道 channel 读取数据给 value ,或者可以说从通道channel接收数据赋值给value。顺便提及其他的写法<- channel,显然也是从通道读取数据,但是没有赋值给任何一个变量,因此表示丢弃!
关于channel的阻塞问题,我想有必要着重梳理下。请看下面的总结
首先通道的接收和发送都是阻塞的,除非与之对应的一端已经准备好。通过举例来说明是最好不过了。
-
新建一个通道,channel_c := make(chan int),注意是没有分配缓存的。
-
随后往通道写入数据,channel_c <- value,由于没有分配缓存,因此这里会被堵塞掉,换句话说就是卡死在这里,只能等待,等待程序某个地方从该通道读取数据!(ps:就好比非常短的管 子,就往里塞了一个数据,就要漏出来,因此我一直手”扶着”数据,腾不出手干下面的事了。只有当接收方来拿数据了才能腾出手来干别的事。)
-
2015.04.22 -> 对上面比喻修改。ps:上面比喻的不是很恰当,我想是不是可以这么认为,channel是连通两个routine的通道,当发送者向channel里发送 数据,却迟迟等不到接收者,但秉着尽职尽责的思想,始终等待在那个位置,即channel_c := make(chan int)这条发送语句处,直到接收者接收了通道数据,才可以进行下一条语句。那么假如有缓存就不同了,就好比一个驿站,拿着数据到通道这发送“邮件”,尽 管可能没有接收者,但是我却可以先暂时放到“驿站”(缓存)这里,然后继续干自己的活,等下次再向通道发送数据时发现缓存满了,就只能耐心等待,除非这时 有人来取数据,腾出缓存空间了,才可以写入数据。
-
现在换成从通道读取数据,v := <- channel_c,分两种情况:当channel_c通道中有数据时,那肯定不会堵塞,很顺利的读过来;当channel_c通道没有数据时,那么自然又堵死了,除非程序其它地方往通道写入数据了!
-
最后讲讲关于分配了缓存的通道,例如:channel := make(chan int,2)。假如这时候往通道写入数据channel <- 2,由于分配了缓存,意味着是我可以直接写入,不会堵塞了,但这仅当我的缓存区未满的情况下才不会堵塞。下面往通道写入一个channel <- 3,这是没有问题的。不过缓存已经填满了!假如这时候你再写的话就会堵塞,只有程序某个地方从改通道读取数据腾出地方了,你才可以写入!
有疑问加站长微信联系(非本文作者)