Channel可以想像成是一种资料结构,可以push data进去也可以pull data出来。
因为Channel会等待另一端完成Push/Pull的动作才会继续往下处理,而且特性使其可以在Goroutines间同步处理的资料,而不用使用lock, unlock等方式。
这里示范建立一个int型别的channel
ch := make(chan int)
将data d 送入channel ch之中
ch <- d
将data从channel ch拿出,并赋予给变数d
d := <- ch
Channel是用来让Goroutine沟通时使用的一种资料结构,并且由於其阻塞的特性,它也能够当成一种等待goroutine的方法。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go calculator(ch)
time.Sleep(3 * time.Second)
fmt.Println(<-ch)
time.Sleep(time.Second)
fmt.Println("main goroutine finished")
}
func calculator(ch chan string) {
fmt.Println("Start to calculate goroutines")
time.Sleep(time.Second)
fmt.Println("Stop to calculate goroutines")
ch <- "FINISH"
fmt.Println("Finish calculator")
}
运行後可得以下结果
Start to calculate goroutines
Stop to calculate goroutines
FINISH
Finish calculator
Main goroutine finished
那我们这边的三秒延迟目的是为了让main thread慢於goroutines,一秒延迟则是模拟goroutines的作业时间! 所以他会依序的去进行goroutines作业→打印channel的内容→完成goroutines function→完成main thread。
通道分为两种,有buffer与无buffer的,也就是有储存空间限制的channel与无限制的channel。
此外,通道也能分为单向与双向两种,就是能传资料的caller
vs只有一方能传资料的callee
。
Variable := make(chan Type)
chan
被制造出来之後,需要传入 要被你并发出去的func
,它靠这种方式传资料。
chan
是有方向性的,要看箭头←
的方向。
chan <- A `把 A这个东西 塞进chan`
B<- chan `从chan 挖东西出来 到B`
由下两范例来说明接收的方向性:
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func1(ch)
ch <- 100
}
func func1(ch chan int) {
i := <-ch
fmt.Println(i)
}
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func2(ch)
got := <-ch
fmt.Println(got)
}
func func2(ch chan int) {
time.Sleep(time.Second * 2)
ch <- 999
}
运行後可得以下结果
999
由上面例子我们可以得知,Unbuffered Channel有一个特性:
也因此如果Pusher的执行一次时间较Puller短,会造成Pusher被迫等待Puller拉取才能进行下一次的push,而这样的等待是相当浪费时间的。
为了解决此问题,因此诞生了另一种Channel,Buffered Channel。
Variable := make(chan Type, Number)
有限制储存空间的通道,若限制放两个,就只能有两笔数据,倘若塞入第三笔的话则会造成死结DeadLock
package main
func main() {
ch := make(chan int, 2)
go func3(ch)
ch <- 100
ch <- 99
ch <- 98 // 发生deadlock
}
func func3(ch chan int) {
}
运行後可得以下结果
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox241095799/prog.go:9 +0xa7
以上例来说,通常chan塞不下第三笔数据时,只会发生Block(阻塞滞留)
,而当Block
永远无法解开的情况发生,则是 Deadlock(死结)
。上面会发生死结是因为 不论等多久,都不会从Block
的状态中脱离。
只要通道(Chan)
塞不下,或者没东西可挖,都会发生Block
阻塞。
以下是通道Channel 阻塞Block
的例子
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go func1(ch)
for i := 0; i < 10; i++ {
ch <- i
fmt.Println("main sent", i)
}
time.Sleep(time.Second)
}
func func1(ch chan int) {
for {
i := <-ch
fmt.Println("func1 got", i)
time.Sleep(time.Millisecond * 100)
}
}
运行後可得以下结果
main sent 0
main sent 1
main sent 2
func1 got 0
func1 got 1
main sent 3
func1 got 2
main sent 4
func1 got 3
main sent 5
func1 got 4
main sent 6
func1 got 5
main sent 7
func1 got 6
main sent 8
func1 got 7
main sent 9
func1 got 8
func1 got 9
主程序不间断地连续塞十次数字
送完休息1秒;而func1
每0.1秒吃下来一个数值。
虽然慢,但程序不会打死结
,只是会造成执行效率低下而已。
如果把Buffer Size: 2换成5
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go func1(ch)
for i := 0; i < 10; i++ {
ch <- i
fmt.Println("main sent", i)
}
time.Sleep(time.Second)
}
func func1(ch chan int) {
for {
i := <-ch
fmt.Println("func1 got", i)
time.Sleep(time.Millisecond * 100)
}
}
运行後可得以下结果
main sent 0
main sent 1
main sent 2
main sent 3
main sent 4
main sent 5
func1 got 0
func1 got 1
main sent 6
func1 got 2
main sent 7
func1 got 3
main sent 8
func1 got 4
main sent 9
func1 got 5
func1 got 6
func1 got 7
func1 got 8
func1 got 9
同时间通道里最多会有五个数字。
塞与取的先後顺序,透过log.SetFlags(5)
来看会比较清楚。
The buffer size is the number of elements that can be sent to the channel without the send blocking.
Buffer 是拿来缓冲用的,Unbuffered Channel则是0缓冲
,就是没有缓冲啦!Unbuffered 是需要有同时有一头写入、另一头读出,才能动的。
当然是 没有!
因为倘若需要给个100000byte大小的Channel必须先挪出100000byte的记忆体空间出来,如此一来多创几个Unlimited Chaneel就会造成Out Of Memory了!
这章节我们讲述了Channel的用法,Unbuffered与Buffered的差别,彼此所遇到的问题与困难,以及它们各自搭配goroutines的使用范例,
再过一天,比赛就结束了。时间真的过得好快啊! 今晚我从 open data 网站,下载...
Layer A logical grouping of data, similar to trans...
Kneron - Kneron Toolchain 转档操作参考笔记 参考资料 onnx 档案来源:...
题组回顾与观念统整 Stack 和 Queue 绝对是资料结构中不可以错过的一种容器,不只用於资料...
Portal 提供一个优秀方法来让 children 可以 render 到 parent com...