本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!