我对 golang 还很陌生,所以我确信这个问题很大程度上是由于我的一些概念上的缺陷。
在 golang 中,我们可以有两种类型的通道:无缓冲和有缓冲(分别为同步和异步)。
unbufferedChan := make(chan string)
bufferedChan := make(chan string, 100)
通过无缓冲通道进行通信的两个 goroutine 必须相互等待。也就是说,接收 goroutine 阻塞直到发送者发送,发送者阻塞直到接收者接收。
在缓冲的情况下,仅当通道为空时接收器才会阻塞。仅当通道已满时,发送方才会阻塞。
通过使用缓冲通道,我希望减少 goroutine 阻塞的时间,从而提高代码的速度。相反,一切都变得更慢了。与我的预期相反(即“缓冲更有效”),无缓冲通道最终变得更快。
这是所有的代码。你需要go get code.google.com/p/go-html-transform/html/transform
先。
package main
import (
"fmt"
"math/rand"
"os"
"sync"
"time"
sel "code.google.com/p/go-html-transform/css/selector"
h5 "code.google.com/p/go-html-transform/h5"
gnhtml "code.google.com/p/go.net/html"
)
// Find a specific HTML element and return its textual element children.
func main() {
test := `
<html>
<head>
<title>This is the test document!</title>
<style>
header: color=blue;
</style>
</head>
<body>
<div id="h" class="header">This is some text</div>
</body>
</html>`
// Get a parse tree for this HTML
h5tree, err := h5.NewFromString(test)
if err != nil {
die(err)
}
n := h5tree.Top()
// Create a Chain object from a CSS selector statement
chn, err := sel.Selector("#h")
if err != nil {
die(err)
}
// Find the item. Should be a div node with the text "This is some text"
h := chn.Find(n)[0]
// run our little experiment this many times total
var iter int = 100000
// When buffering, how large shall the buffer be?
var bufSize uint = 100
// Keep a running total of the number of times we've tried buffered
// and unbuffered channels.
var bufCount int = 0
var unbufCount int =0
// Keep a running total of the number of nanoseconds that have gone by.
var bufSum int64 = 0
var unbufSum int64 = 0
// Call the function {iter} times, randomly choosing whether to use a
// buffered or unbuffered channel.
for i := 0; i < iter; i++ {
if rand.Float32() < 0.5 {
// No buffering
unbufCount += 1
startTime := time.Now()
getAllText(h, 0)
unbufSum += time.Since(startTime).Nanoseconds()
} else {
// Use buffering
bufCount += 1
startTime := time.Now()
getAllText(h, bufSize)
bufSum += time.Since(startTime).Nanoseconds()
}
}
unbufAvg := unbufSum / int64(unbufCount)
bufAvg := bufSum / int64(bufCount)
fmt.Printf("Unbuffered average time (ns): %v\n", unbufAvg)
fmt.Printf("Buffered average time (ns): %v\n", bufAvg)
}
// Kill the program and report the error
func die(err error) {
fmt.Printf("Terminating: %v\n", err.Error())
os.Exit(1)
}
// Walk through all of a nodes children and construct a string consisting
// of c.Data where c.Type == TextNode
func getAllText(n *gnhtml.Node, bufSize uint) string {
var texts chan string
if bufSize == 0 {
// unbuffered, synchronous
texts = make(chan string)
} else {
// buffered, asynchronous
texts = make(chan string, bufSize)
}
wg := sync.WaitGroup{}
// Go walk through all n's child nodes, sending only textual data
// over the texts channel.
wg.Add(1)
nTree := h5.NewTree(n)
go func () {
nTree.Walk(func (c *gnhtml.Node) {
if c.Type == gnhtml.TextNode {
texts <- c.Data
}
})
close(texts)
wg.Done()
}()
// As text data comes in over the texts channel, build up finalString
wg.Add(1)
finalString := ""
go func () {
for t := range texts {
finalString += t
}
wg.Done()
}()
// Return finalString once both of the goroutines have finished.
wg.Wait()
return finalString
}
运行程序 10 次,结果如下:
Buffered average time (ns): 32088
Buffered average time (ns): 32183
Buffered average time (ns): 35091
Buffered average time (ns): 35798
Buffered average time (ns): 36966
Buffered average time (ns): 38707
Buffered average time (ns): 39464
Buffered average time (ns): 40021
Buffered average time (ns): 41063
Buffered average time (ns): 46248
Unbuffered average time (ns): 18265
Unbuffered average time (ns): 18804
Unbuffered average time (ns): 20268
Unbuffered average time (ns): 20401
Unbuffered average time (ns): 21652
Unbuffered average time (ns): 22630
Unbuffered average time (ns): 22907
Unbuffered average time (ns): 23326
Unbuffered average time (ns): 24133
Unbuffered average time (ns): 27546