资源占用方面,goroutine 会从4096字节的初始栈内存占用开始按需增长或缩减内存占用。同步传输效率方面,我曾经在松本行弘的《代码的未来》一书上看到一个简洁的例子(书上的代码中行末带有分号,目前golang中已经取消了源代码中行末的分号,由编译器代为添加,一行代码包含多个语句则需要分号分隔)。下列代码在原代码基础上做了适当调整。
package main
import (
"fmt"
"time"
)
func chanFlow(left, right chan int, bufferLen int) {
if bufferLen <= 0 {
left <- 1 + <-right
} else {
for i := 0; i < bufferLen; i++ {
left <- 1 + <-right
}
}
}
func main() {
nruntime := 100000
lastChan := make(chan int)
var left chan int = nil
right := lastChan
begin := time.Now()
fmt.Println("begin at:", begin)
for i := 0; i < nruntime; i++ {
left, right = right, make(chan int)
go chanFlow(left, right, 0)
}
right <- 0
result := <-lastChan
end := time.Now()
fmt.Println("end at:", end, time.Since(begin))
fmt.Println(result)
}
程序创建了10w零1个无缓冲channel, 10w个goruntime, 数据在goruntime中从第一个channel流向最后一个channel,每流入一次数值加一。代码在我的笔记本(2.7 GHz Intel Core i5, 8 GB 1867 MHz DDR3)运行结果如下:
上面的例子中使用的是无缓冲的channel,把代码修改为带1000个单位缓冲的channel再试试看,代码如下:
func chanFlow(left, right chan int, bufferLen int) {...}
func main() {
nruntime := 100000
chanBuffer := 1000
result := make([]int, 0, 100)
lastChan := make(chan int, chanBuffer)
var left chan int = nil
right := lastChan
begin := time.Now()
fmt.Println("begin at:", begin)
for i := 0; i < nruntime; i++ {
left, right = right, make(chan int, chanBuffer)
go chanFlow(left, right, chanBuffer)
}
for i := 0; i < chanBuffer; i++ {
right <- 0
}
for i := 0; i < chanBuffer; i++ {
result = append(result, <-lastChan)
}
end := time.Now()
fmt.Println("end at:", end, time.Since(begin))
fmt.Println(result)
}
运行结果如下:
而在实际生产中,更多的需要传递的数据是字符串,那么现在把代码再修改一下试试,代码如下:
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"time"
)
func chanFlow(left, right chan string, bufferLen int) {
if bufferLen <= 0 {
left <- <-right
} else {
for i := 0; i < bufferLen; i++ {
left <- <-right
}
}
}
func genString() string {
b := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
} else {
return base64.URLEncoding.EncodeToString(b)
}
}
func main() {
nruntime := 100000
chanBuffer := 1000
result := make([]string, 0, 100)
lastChan := make(chan string, chanBuffer)
dataForChan := make([]string, 0, chanBuffer)
for i := 0; i < chanBuffer; i++ {
dataForChan = append(dataForChan, genString())
}
var left chan string = nil
right := lastChan
begin := time.Now()
fmt.Println("begin at:", begin)
for i := 0; i < nruntime; i++ {
left, right = right, make(chan string, chanBuffer)
go chanFlow(left, right, chanBuffer)
}
for i := 0; i < chanBuffer; i++ {
right <- dataForChan[i]
}
for i := 0; i < chanBuffer; i++ {
result = append(result, <-lastChan)
}
end := time.Now()
fmt.Println("end at:", end, time.Since(begin))
fmt.Println(result)
}
运行结果如下:
begin at: 2016-08-28 15:06:25.349599328 +0800 CST