带缓存的管道(buffered channel)
上一节我们讨论的channel都是不带缓存的,这里所谓的不带缓存的意思是写进去一个数据,就必须先读取走才能接着写下一个数据进去,因此也是阻塞的。
Golang中我们还可以创建带缓存的channel,向带缓存的channel中写数据时,只有写满缓存才会阻塞协程的执行;同样的,从带缓存的channel中读数据时,只有缓存为空时,即已经没数据可读时才会阻塞协程的执行。
可以通过如下语法来创建带缓存的channel:
ch := make(chan type, capacity)这里的capacity表示缓存容量,必须大于或等于1才有缓存,默认的不带缓存的channel缓存容量为0。
带缓存channel举例
channel举例我们举个例子来体会下带缓存channel的用法。
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "naveen"
ch <- "paul"
fmt.Println(<-ch)
fmt.Println(<-ch)
}这里我们通过语句ch := make(chan string, 2)创建了缓存容量为2的channel。由于容量为2,我们可以向该channel中写入2个数据而不阻塞协程的执行。我们写入了两个字符串,由于没有阻塞,因此后面的打印语句得以执行。该程序输出结果如下:
naveen
paul我们再看一个例子来加深下对带缓存channel的理解。
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("successfully wrote", i, "to ch")
}
close(ch)
}
func main() {
ch := make(chan int, 2)
go write(ch)
time.Sleep(2 * time.Second)
for v := range ch {
fmt.Println("read value", v, "from ch")
time.Sleep(2 * time.Second)
}
}这里我们创建一个缓存容量为2的channel。开启了write协程后,主协程暂停了2秒。这期间write协程在并发执行,在write协程内我们不断的向channel内写入数据,但是由于channel的容量为2,我们仅能先写进去2个数据,即0和1,然后该协程进入阻塞状态,直到channel内的数据被读走。因此该程序先输出如下:
successfully wrote 0 to ch
successfully wrote 1 to ch然后主协程暂停2秒后开始读取channel内的数据,每读出一个数据主协程又要暂停2秒,这个暂停期间write协程中发现channel内多了一个缓存位置,便继续写入一个数据,然后缓存又变满,再次进入阻塞状态。该程序输出如下:
successfully wrote 0 to ch
successfully wrote 1 to ch
read value 0 from ch
successfully wrote 2 to ch
read value 1 from ch
successfully wrote 3 to ch
read value 2 from ch
successfully wrote 4 to ch
read value 3 from ch
read value 4 from ch死锁
上一节我们已经介绍过死锁了:
如果某个
goroutine向某个channel发送数据,那么需要某个其他goroutine接收该channel的数据,否则会发送运行时错误:deadlock。类似的,如果某个goroutine正在等待从某个channel中接收数据,那么需要某个其他goroutine向该channel中写入数据,否则也会发送运行时错误:deadlock。
上一节我们讨论的channel都是不带缓存的,因此前面的说法是成立的。对于带缓存的channel,死锁条件则是缓存满了,或者缓存空了。就是说,如果channel缓存已经满了,再继续向channel写入数据时,那么就需要存在某个其他goroutine接收该channel的数据,否则陷入死锁状态;如果channel缓存已经空了,再继续从该channel中读取数据时,需要存在某个其他goroutine向该channel中写入数据,否则陷入死锁状态。
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "naveen"
ch <- "paul"
ch <- "steve"
fmt.Println(<-ch)
fmt.Println(<-ch)
}这里我们向一个缓存容量为2的channel中写入了3个数据。这时必须有某个goroutine从该channel中读取数据才行,但是并没有,因此陷入死锁状态。该程序执行报如下错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/goWorkspace/src/test/main.go:9 +0x90
exit status 2channel长度 vs channel容量
channel长度 vs channel容量channel容量指的是channel最多能缓存的数据个数,是我们用make初始化channel时的第二个参数。channel长度指的是channel当前时刻缓存的数据个数。
package main
import "fmt"
func main() {
ch := make(chan string, 3)
ch <- "naveen"
ch <- "paul"
fmt.Println("capacity is", cap(ch))
fmt.Println("length is", len(ch))
fmt.Println("read value", <-ch)
fmt.Println("new length is", len(ch))
}这里我们创建了一个容量为3的channel,因此最多能缓存3个数据。接着我们写进去2个数据,这样缓存内就有2个数据,因此长度为2。然后又读取了1个数据,这时还剩下1个数据,因此长度为1。该程序执行结果如下:
capacity is 3
length is 2
read value naveen
new length is 1Last updated
Was this helpful?