1. Channels
| Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine. |
Create a new channel with make(chan val-type). Channels are typed by the values they convey.
Send a value into a channel using the channel <- syntax.
The <-channel syntax receives a value from the channel.
When we run the program the "ping" message is successfully passed from one goroutine to another via our channel.
By default sends and receives block until both the sender and receiver are ready. This property allowed us to wait at the end of our program for the "ping" message without having to use any other synchronization.
[maxwell@oracle-db-19c Day04]$ vim channels.go
[maxwell@oracle-db-19c Day04]$ cat channels.go
package main
import "fmt"
func main(){
messages := make(chan string)
go func() {messages <- "ping"}()
msg := <-messages
fmt.Println(msg)
}
[maxwell@oracle-db-19c Day04]$ go run channels.go
ping
[maxwell@oracle-db-19c Day04]$
2.Channel Buffering
By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value. Buffered channels accept a limited number of values without a corresponding receiver for those values.
[maxwell@oracle-db-19c Day04]$ vim channelbuffering.go
[maxwell@oracle-db-19c Day04]$ cat channelbuffering.go
package main
import "fmt"
func main(){
messages := make(chan string, 2)
messages <- "Buffered"
messages <- "channel"
fmt.Println(<-messages)
fmt.Println(<-messages)
}
[maxwell@oracle-db-19c Day04]$ go run channelbuffering.go
Buffered
channel
[maxwell@oracle-db-19c Day04]$
3. Channel Synchronization
We can use channels to synchronize execution across goroutines. Here’s an example of using a blocking receive to wait for a goroutine to finish. When waiting for multiple goroutines to finish, you may prefer to use a WaitGroup.
[maxwell@oracle-db-19c Day04]$ vim channelsynchronization.go
[maxwell@oracle-db-19c Day04]$ cat channelsynchronization.go
package main
import (
"fmt"
"time"
)
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("Done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done
}
[maxwell@oracle-db-19c Day04]$ go run channelsynchronization.go
working...Done
[maxwell@oracle-db-19c Day04]$
4.Channel Directions
When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program.
This ping function only accepts a channel for sending values. It would be a compile-time error to try to receive on this channel.
The pong function accepts one channel for receives (pings) and a second for sends (pongs).
[maxwell@oracle-db-19c Day04]$ vim channeldirections.go
[maxwell@oracle-db-19c Day04]$ cat channeldirections.go
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)
}
[maxwell@oracle-db-19c Day04]$ go run channeldirections.go
passed message
[maxwell@oracle-db-19c Day04]$
5.Select
Go’s select lets you wait on multiple channel operations. Combining goroutines and channels with select is a powerful feature of Go.
Each channel will receive a value after some amount of time, to simulate e.g. blocking RPC operations executing in concurrent goroutines.
We’ll use select to await both of these values simultaneously, printing each one as it arrives.
Note that the total execution time is only ~2 seconds since both the 1 and 2 second Sleeps execute concurrently.
[maxwell@oracle-db-19c Day04]$ vim select.go
[maxwell@oracle-db-19c Day04]$ cat select.go
package main
import (
"fmt"
"time"
)
func main(){
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)
}
}
}
[maxwell@oracle-db-19c Day04]$ time go run select.go
received one
received two
real 0m2.128s
user 0m0.101s
sys 0m0.086s
[maxwell@oracle-db-19c Day04]$
本文介绍了Go语言中通道(Channels)的核心概念和用法,包括无缓冲通道的同步特性,通过缓冲通道实现数据传递,使用通道进行同步以等待goroutine完成,以及利用选择器(Select)等待多个通道操作。这些机制是Go语言并发编程的关键工具。

被折叠的 条评论
为什么被折叠?



