channel是Go语言中的一个核心数据类型,channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题。在并发核心单元通过它就可以发送或者接收数据进行通讯,这在一定程度上又进一步降低了编程的难度。
goroutine运行在相同的内存地址空间,channel可以避开所有内存共享导致的坑;通道的通信方式保证了同步性。数据通过channel:同一时间只有一个协程可以访问数据:所以不会出现数据竞争,确保并发安全。
channel是对应make创建的底层数据结构的引用。 创建语法: make(chan Type, capacity)
channel := make(chan bool) //创建一个无缓冲的bool型Channel?,等价于make(chan Type, 0)
channel := make(chan bool, 1024) //创建一个有缓冲,切缓冲区为1024的bool型Channel?
channel <- x //向一个Channel发送一个值
<- channel //从一个Channel中接收一个值
x = <- channel //从Channel c接收一个值并将其存储到x中
x, ok = <- channel //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false
channel是一个引用类型,当复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值(定义未初始化)也是nil。
在默认情况下,channel接收和发送数据都是阻塞的,(channel <- 1
,写端写数据,读端不在读。写端阻塞; str := <-channel
读端读数据, 同时写端不在写,读端阻塞。)除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock。
示例
package main
import (
"fmt"
"runtime"
"time"
)
var c = make(chan int32)
func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
time.Sleep(time.Second)
c <- value
}
}
func main() {
runtime.GOMAXPROCS(1)
go func() {
time.Sleep(time.Second)
printstr("hello")
}()
go func() {
for v := range c {
fmt.Printf("读取%+q\r\n", v)
}
}()
for {
;
}
}
无缓冲的channel unbuffered channel
是指在接收前没有能力保存任何值的通道。这种类型的channel 要求发送端和接收端同时准备好,才能完成发送和接收操作。否则,通道会导致先执行发送或接收操作的阻塞等待。顾又称为同步通信
示例如上,写了没有读会导致阻塞,读了没有写会导致堵塞
有缓冲的通道(buffered channel
)是一种在被接收前能存储一个或者多个数据值的通道。这种类型的channel并不强制要求goroutine
之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也不同。
有缓冲的channel和无缓冲的channel之间的不同:无缓冲的channel保证进行发送和接收的 goroutine
会在同一时间进行数据交换;有缓冲的channel没有这种保证。
示例
package main
import (
"fmt"
"runtime"
"time"
)
var c = make(chan int32, 10)
func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
c <- value
}
}
func main() {
runtime.GOMAXPROCS(1)
go func() {
printstr("hello")
}()
go func() {
time.Sleep(time.Second * 2)
fmt.Println("读通道开始读取数据")
for v := range c {
fmt.Printf("读取%+q\r\n", v)
}
}()
for {
}
}
结果可以看出,如果给定了一个缓冲区容量,channel就是异步的。只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行。
当发送的一端没有更多的数据发送到channel的话,需要使接收端也能及时知道channel中没有多余的数据可以接收。因此可以通过 close()
函数来关闭channel的实现。
示例
package main
import (
"fmt"
"runtime"
"time"
)
var c = make(chan int32, 10)
func printstr(s string) {
for _, value := range s {
fmt.Printf("写入%+q\r\n", value)
c <- value
}
close(c)
}
func main() {
runtime.GOMAXPROCS(1)
go func() {
printstr("hello")
}()
time.Sleep(time.Second * 2)
fmt.Println("读通道开始读取数据")
for {
if char, ok := <-c; ok {
fmt.Printf("读取%+q\r\n", char)
} else {
break
}
}
}
提示
- channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;
- 关闭channel后,无法向channel 再发送数据(引发 panic 错误后导致接收立即返回零值);
- 关闭channel后,可以继续从channel接收数据(读取到的数据为channel类型的默认值,如int默认值0 string默认值"");
- 对于nil channel,无论收发都会被阻塞。
make(chan TYPE,CAPCTIY)
,非缓冲channel的创建方式为make(chan TYPE)
原文:https://www.cnblogs.com/LC161616/p/13866321.html