Goroutines and Channels and Select

2024-02-12 22:08
文章标签 channels select goroutines

本文主要是介绍Goroutines and Channels and Select,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

多线程

首先创建多线程方法:

func f(from string) {for i := 0; i < 3; i++ {fmt.Println(from, ":", i)}
}
f("direct")
go f("goroutine")
go func(msg string) {fmt.Println(msg)
}("going")
time.Sleep(time.Second)
fmt.Println("done")

direct : 0
direct : 1
direct : 2
going
goroutine : 0
goroutine : 1
goroutine : 2
done

通道

通道是连接并发线程的管道。一个线程将值发送到管道,另一个线程从管道中接收该值。

messages := make(chan string)
go func() {time.Sleep(time.Second)messages <- "ping"
}()
msg := <-messages
fmt.Println(msg)

ping

msg一直等待messages通道返回数据,直到messages通道传入数据。

注意,该通道是无缓冲的,这意味着,发送的值,只有当相应的接收者<- chan准备接收时,通道才会允许发送chan <-

缓冲通道

messages := make(chan string, 2)
messages <- "buffered"
messages <- "channel"
fmt.Println(<-messages)
fmt.Println(<-messages)

buffered
channel

缓冲通道接收指定数量的值,即使不存在相应的接收者接收这些值。

通道同步执行

func worker(done chan bool) {fmt.Print("working...")time.Sleep(time.Second)fmt.Println("done")done <- true
}
done := make(chan bool, 1)
go worker(done)
<-done

working…done

通道可用于跨线程同步执行。上面的代码中,通道阻塞了主线程的运行,并在等待多线程运行完毕时,继续运行主线程。

通道方向

当通道作为参数使用时,可以指定通道参数仅是发送者还是接收者。

func ping(pings chan<- string, msg string) {pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {msg := <-pingspongs <- msg
}
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)

passed message

以上代码中,pings通道用于接收数据,pongs通道用于发送数据。ping方法将数据传给pings;pong方法将数据从pings传到pongs(这里pings作为发送者,pongs作为接收者)。最后pongs将数据传出。

选择器

选择器用于等待多个通道的活动。

c1 := make(chan string)
c2 := make(chan string)
go func() {time.Sleep(1 * time.Second)c1 <- "one"
}()
go func() {time.Sleep(2 * time.Second)c2 <- "two"
}()
for i := 0; i < 2; i++ {select {case msg1 := <-c1:fmt.Println("received", msg1)case msg2 := <-c2:fmt.Println("received", msg2)}
}

received one
received two

select代码块每次等待且仅等待一个channel返回数据。第一个channel延迟1秒后传入数据,select的case立即做出响应并执行,然后select结束执行;下一个select开始等待执行,直到第二个channel延迟2秒后传入数据,select的case立即作出响应并执行,select结束执行。

超时

select和channel配合使用时,可能会有等待过程。等待时间过长时,需要做超时处理。

c := make(chan string, 1)
go func() {time.Sleep(2 * time.Second)c <- "result 1"
}()
select {
case res := <-c:fmt.Println(res)
case <-time.After(1 * time.Second):fmt.Println("timeout 1")
}

timeout 1

以上代码中,select分别等待两个channel;其中第二个case先执行,select不再等待第一个case并结束。

c := make(chan string, 1)
go func() {time.Sleep(2 * time.Second)c <- "result 2"
}()
select {
case res := <-c:fmt.Println(res)
case <-time.After(3 * time.Second):fmt.Println("timeout 2")
}

result 2

以上代码中,select分别等待两个channel;其中第一个case先执行,select不再等待第二个case并结束。

注意,time.After的返回数据是<-chan Time类型。

非阻塞通道操作

通道的基本发送和接收操作是阻塞式的,然而有时候并不希望代码运行时被阻塞,此时可以使用select的default子句实现非阻塞发送接收操作。

messages := make(chan string)
select {
case msg := <-messages:fmt.Println("reveived message", msg)
default:fmt.Println("no message received")
}

no message received

因为通道没有数据,也无法返回数据,所以select不执行case代码块,而执行default代码块。

messages := make(chan string)
signals := make(chan bool)
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")
}

no message sent
no activity

以上代码中,通道无法接收数据,这是因为通道既没有缓冲区,也没有接收者。

messages := make(chan string, 1)
signals := make(chan bool)
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")
}

sent message hi
received message hi

以上代码中,通道设置了缓冲区,第一个select的case代码块被执行;接收者也能接收通道发送的数据,第二个select的第一个case代码块被执行。

通道关闭

关闭通道表示不再有数据发送给通道。这有助于向通道的接收器传达信息,表明通道已完成。

jobs := make(chan int, 5)
done := make(chan bool)
go func() {for {j, more := <-jobsif more {fmt.Println("received job", j)} else {fmt.Println("received all jobs")done <- truereturn}}
}()
for j := 1; j <= 3; j++ {jobs <- jfmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
<-done

sent job 1
sent job 2
sent job 3
sent all jobs
received job 1
received job 2
received job 3
received all jobs

close方法调用时,通道向接收器发送信息,表明工作已完成。最后使用通道同步执行(变量名为done的通道),保证在多线程执行完成后,主线程才会结束。

注意,通道即使关闭,接收器依然可以接收通道的值。

通道遍历

range也可迭代获取通道接收的值。

queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
for elem := range queue {fmt.Println(elem)
}

one
two

这篇关于Goroutines and Channels and Select的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/703720

相关文章

easyui 验证下拉菜单select

validatebox.js中添加以下方法: selectRequired: {validator: function (value) {if (value == "" || value.indexOf('请选择') >= 0 || value.indexOf('全部') >= 0) {return false;}else {return true;}},message: '该下拉框为必选项'}

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

C++ I/O多路复用 select / poll / epoll

I/O多路复用:在网络I/O中,用 1个或1组线程 管理 多个连接描述符。             如果有至少一个描述符准备就绪,就处理对应的事件             如果没有,就会被阻塞,让出CPU给其他应用程序运行,直到有准备就绪的描述符 或 超时

Go Select的实现

select语法总结 select对应的每个case如果有已经准备好的case 则进行chan读写操作;若没有则执行defualt语句;若都没有则阻塞当前goroutine,直到某个chan准备好可读或可写,完成对应的case后退出。 Select的内存布局 了解chanel的实现后对select的语法有个疑问,select如何实现多路复用的,为什么没有在第一个channel操作时阻塞 从而导

Go 语言中Select与for结合使用break

func test(){i := 0for {select {case <-time.After(time.Second * time.Duration(2)):i++if i == 5{fmt.Println("break now")break }fmt.Println("inside the select: ")}fmt.Println("inside the for: ")}} 执行后

select、poll、epoll的区别

select、poll、epoll均为linux中的多路复用技术。3种技术出现的顺序是select、poll、epoll,3个版本反应了多路复用技术的迭代过程。我们现在开发网络应用时, 一般都会使用多路复用,很少有用一个线程来监听一个fd的,其中epoll又是最常使用的。关于epoll的实现和常见问题可以参考epoll实现原理和常见问题总结。 当我们在使用epoll的时候,会想当然的认为这种技术

js 获取select的值 / js动态给select赋值

正常使用的: var month =  $('#month option:selected').val();//选中的值 var a = $('#month option:selected').text();//选中的文本 var b = $('#month option:selected') .val();//选中的值 var c = $("#month").get(0).selected

Superset二次开发之Select 筛选器源码分析

路径:superset-frontend/src/filters/components/Select  源码文件: 功能点: 作用 交互 功能 index.ts作为模块的入口点,导出其他文件中定义的主要组件和函数。它使其他文件中的导出可以被外部模块使用。 SelectFilterPlugin.tsx 定义主要的插件类 SelectFilterPlugin 和组件 Sele

SQL SELECT常用语句

SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL)。 SQL (结构化查询语言)是用于执行查询的语法。但是 SQL 语言也包含用于更新、插入和删除记录的语法。 查询和更新指令构成了 SQL 的 DML 部分: •SELECT - 从数据库表中获取数据 •UPDATE - 更新数据库表中的数据 •DELETE - 从数

select poll epoll之间的区别比较

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核