golang的channel剖析
wslzwps00321 · · 1763 次点击 · · 开始浏览概述
为了并发的goroutines之间的通讯,golang使用了管道channel。你可以通过一个goroutines向channel发送数据,然后从另一个goroutine接收它
通常我们会使用make来创建channel ----- make(chan valType , [size])。
写入 channel c<-
读取channel <-c
分类
一 无缓冲
make创建时忽略第二个参数即可创建无缓冲channel实例如下:
// csdnTest.go
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan string)
go func() {
messages <- "hello"
}()
msg := <-messages
fmt.Println(msg)
}值得注意的是:无缓冲channel只有接收和发送方都准备好了,channel才能正常工作,上述代码中如果我们把msg := <-messages这句注释掉,那么messages <- "hello"将会一直阻塞。
在这里做个猜想: 无缓冲的buffer只是直使用了ipc的pipe,pipe两端都打开时才能使用。"<-"被解释为打开写端,"->被解释为打开读端"
二 有缓冲
示例:
// csdnTest.go
package main
import (
"fmt"
)
func main() {
messages := make(chan string, 2)
messages <- "hello"
messages <- "world"
fmt.Println(<-msg)
fmt.Println(<-msg)
}说明:当创建有缓冲的buffer时,就不需要有准备好的接收器来接受数据,数据可以存在channel的缓冲区中,有缓冲的channel有如下特性:
1:数据未满可以写,当数据满了再写阻塞。
2:数据未空可以读,当数据空了再读阻塞。
通过上面的特性可以做个大胆的猜测,有缓冲的是通过msg-queue来实现的。
有缓冲和无缓冲从底层的实现就不同,因为他们使用了不同的ipc。
三 非阻塞
正常情况下的channel读和写都是阻塞的,然而我们可以使用一个带有default的select来触发非阻塞的channle。原本select也是阻塞的,在加入default时select本身也变为非阻塞
举个例子:
// csdnTest.go
package main
import "fmt"
func main() {
messages := make(chan string)
signals := make(chan bool)
//message和signals是无缓冲channel,因为接收器和发送器没同时准备好,所以channel无法使用。
//下面所有select会输出default的内容
select {
case msg := <-messages:
fmt.Println("received message", msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
}有兴趣同学将上面无缓冲的改成有缓冲的尝试一下。
至于select为何在加入default后channel和自身都未非阻塞了,你可以去看看go源码select.go这个源文件,-------------------也可以阅读我的下一期博客。
四 channel的用例
4.1 处理超时
c := make(chan string, 1)
go func() {
time.Sleep(time.Second * 3)
c <- "inner goroutine"
}()
select {
case res := <-c:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout")
}
4.2 channel关闭
关闭一个channel时,就不能向该channel中写数据了。通常我们用一个值返回值来接收channel的数据,但此时可以用两个返回值 (valeu,b:=<-c) 来捕获channel是否关闭 b取值false或true。
当我们处理一个需要结束的任务,可采用下面的方式来调度
jobs := make(chan int, 10)
done := make(chan bool)
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)4.3 数据乒乓球式的流转
举一个golang官网比较知名的一个例子:
package main
import "fmt"
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}
谢谢各位的阅读
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
概述
为了并发的goroutines之间的通讯,golang使用了管道channel。你可以通过一个goroutines向channel发送数据,然后从另一个goroutine接收它
通常我们会使用make来创建channel ----- make(chan valType , [size])。
写入 channel c<-
读取channel <-c
分类
一 无缓冲
make创建时忽略第二个参数即可创建无缓冲channel实例如下:
// csdnTest.go
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan string)
go func() {
messages <- "hello"
}()
msg := <-messages
fmt.Println(msg)
}值得注意的是:无缓冲channel只有接收和发送方都准备好了,channel才能正常工作,上述代码中如果我们把msg := <-messages这句注释掉,那么messages <- "hello"将会一直阻塞。
在这里做个猜想: 无缓冲的buffer只是直使用了ipc的pipe,pipe两端都打开时才能使用。"<-"被解释为打开写端,"->被解释为打开读端"
二 有缓冲
示例:
// csdnTest.go
package main
import (
"fmt"
)
func main() {
messages := make(chan string, 2)
messages <- "hello"
messages <- "world"
fmt.Println(<-msg)
fmt.Println(<-msg)
}说明:当创建有缓冲的buffer时,就不需要有准备好的接收器来接受数据,数据可以存在channel的缓冲区中,有缓冲的channel有如下特性:
1:数据未满可以写,当数据满了再写阻塞。
2:数据未空可以读,当数据空了再读阻塞。
通过上面的特性可以做个大胆的猜测,有缓冲的是通过msg-queue来实现的。
有缓冲和无缓冲从底层的实现就不同,因为他们使用了不同的ipc。
三 非阻塞
正常情况下的channel读和写都是阻塞的,然而我们可以使用一个带有default的select来触发非阻塞的channle。原本select也是阻塞的,在加入default时select本身也变为非阻塞
举个例子:
// csdnTest.go
package main
import "fmt"
func main() {
messages := make(chan string)
signals := make(chan bool)
//message和signals是无缓冲channel,因为接收器和发送器没同时准备好,所以channel无法使用。
//下面所有select会输出default的内容
select {
case msg := <-messages:
fmt.Println("received message", msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
}有兴趣同学将上面无缓冲的改成有缓冲的尝试一下。
至于select为何在加入default后channel和自身都未非阻塞了,你可以去看看go源码select.go这个源文件,-------------------也可以阅读我的下一期博客。
四 channel的用例
4.1 处理超时
c := make(chan string, 1)
go func() {
time.Sleep(time.Second * 3)
c <- "inner goroutine"
}()
select {
case res := <-c:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout")
}
4.2 channel关闭
关闭一个channel时,就不能向该channel中写数据了。通常我们用一个值返回值来接收channel的数据,但此时可以用两个返回值 (valeu,b:=<-c) 来捕获channel是否关闭 b取值false或true。
当我们处理一个需要结束的任务,可采用下面的方式来调度
jobs := make(chan int, 10)
done := make(chan bool)
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)4.3 数据乒乓球式的流转
举一个golang官网比较知名的一个例子:
package main
import "fmt"
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}
谢谢各位的阅读