channel
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// Unbuffered Channel of strings.
c := make(chan string)
go boring("boring!", c)
for i := 0; i < 5; i++ {
// Read From Channel - Blocking.
fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value.
}
fmt.Println("You're boring: I'm leaving.")
}
func boring(msg string, c chan string) {
for i := 0; ; i++ {
// Write to Channel.
c <- fmt.Sprintf("%s %d", msg, i) // Expression to be sent can be any suitable value.
// The write does not return until the read from main is complete.
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
main 函数里,首先创建一个 channel 变量,channel 变量必须先创建,只申明,比如 var c chan string,会报错。
用 go 语法起一个boring function,并将 channel c 传递进去,boring function 里面往 channel c 里写入字符串,然后sleep 一段时间,main 函数里,以阻塞的方式去读取 channel c 里的内容。
generator
// Generator: Function that returns a channel
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := boring("boring!") // Function returning a channel.
for i := 0; i < 5; i++ {
fmt.Printf("You say: %q\n", <-c)
}
fmt.Println("You're boring: I'm leaving.")
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
这里拿到 channel c 的方式不是通过 make 定义,而是通过 boring function 来拿到的。boring function 里面通过 for 循环,不断的往 channel c 里塞信息。在main 函数里,有一个 for 循环 receiver 来不断的读取 channel 里的信息,然后程序就这么运行了。
/*
Generator: Function that returns a channel
The boring function returns a channel that lets us communicate with the
boring service it provides.
We can have more instances of the service.
*/
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
joe := boring("Joe")
ann := boring("Ann")
for i := 0; i < 5; i++ {
fmt.Println(<-joe) // Joe and Ann are blocking each other.
fmt.Println(<-ann) // waiting for a message to read.
}
fmt.Println("You're boring: I'm leaving.")
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
有序的 generator,相对于上一个程序,这个程序定义了两个 channel:joe 和 ann,boring 函数跟之前的没有任何区别,main 函数里,循环调用,按顺序阻塞等待一个信息去读取。
multipleplexing
/*
Multiplexing: Let whosoever is ready to talk, talk.
The fanIn function fronts the other channels. Goroutines that are ready to talk
can independently talk without Blocking the other Goroutines. The FanIn channel
receives all messages for processing.
Decouples the execution between the different Goroutines.
Joe ---
\
----- FanIn --- Independent Messages Displayed
/
Ann ---
*/
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c) // Display any message received on the FanIn channel.
}
fmt.Println("You're boring: I'm leaving.")
}
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string) // The FanIn channel
go func() { // This Goroutine will receive messages from Joe.
for {
c <- <-input1 // Write the message to the FanIn channel, Blocking Call.
}
}()
go func() { // This Goroutine will receive messages from Ann
for {
c <- <-input2 // Write the message to the FanIn channel, Blocking Call.
}
}()
return c
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
扇入(fanIn)表示一个模块被多个模块调用。
扇出(fanOut)表示一个模块调用多个模块。
讲两个 channel 被并入一个 channel 进行输出。
sequencing
package main
import (
"fmt"
"math/rand"
"time"
)
// Message contains a channel for the reply.
type Message struct {
str string
wait chan bool // Acts as a signaler
}
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
msg1 := <-c // Waiting on someone (Joe) to talk
fmt.Println(msg1.str)
msg2 := <-c // Waiting on someone (Ann) to talk
fmt.Println(msg2.str)
msg1.wait <- true // Joe can run again
msg2.wait <- true // Ann can run again
}
fmt.Println("You're boring: I'm leaving.")
}
func fanIn(input1, input2 <-chan Message) <-chan Message {
c := make(chan Message) // The FanIn channel.
go func() { // This Goroutine will receive messages from Joe.
for {
c <- <-input1 // Write the message to the FanIn channel, Blocking Call.
}
}()
go func() { // This Goroutine will receive messages from Ann.
for {
c <- <-input2 // Write the message to the FanIn channel, Blocking Call.
}
}()
return c
}
func boring(msg string) <-chan Message { // Returns receive-only (<-) channel of strings.
c := make(chan Message)
waitForIt := make(chan bool) // Give main control over our execution.
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- Message{fmt.Sprintf("%s %d", msg, i), waitForIt}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
<-waitForIt // Block until main tells us to go again.
}
}()
return c // Return the channel to the caller.
}
fanIn 是针对 Message 的消息结构体来进行扇入的,而不是针对单独的 channel 来扇入的。
- channel 的阻塞
<-waitForIt
select
- select
/*
Select is a control structure that is unique to concurrency.
The reason channels and Goroutines are built into the language.
Like a switch but each case is a communication:
-- All channels are evaluated
-- Selection blocks until one communication can proceed, which then does.
-- If multiple can proceed, select choose pseudo-randomly.
-- Default clause, if present, executes immediately if no channel is ready.
Multiplexing: Let whosoever is ready to talk, talk.
The fanIn function fronts the other channels. Goroutines that are ready to talk
can independently talk without Blocking the other Goroutines. The FanIn channel
receives all messages for processing.
Decouples the execution between the different Goroutines.
Joe ---
\
----- FanIn --- Independent Messages Displayed
/
Ann ---
*/
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c) // Display any message received on the FanIn channel.
}
fmt.Println("You're boring: I'm leaving.")
}
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string) // The FanIn channel
go func() { // Now using a select and only one Goroutine
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
}()
return c
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
这里把 fanIn 和 select 结合起来了,在 fanIn 里,进行 select。
- timeout using select
/*
Timeout Using Select
The time.After function returns a channel that blocks for the specified duration.
After the interval, the channel delivers the current time, once.
The select is giving the boring routine 800ms to respond. This will be an endless
loop if boring can perform its work under 800ms every time.
*/
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := boring("Joe")
for {
select {
case s := <-c:
fmt.Println(s)
case <-time.After(800 * time.Millisecond): // This is reset on every iteration.
fmt.Println("You're too slow.")
return
}
}
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
- timeout using select for whole conversation
/*
Timeout Using Select
Create the timer once, outside the loop, to time out the entire conversation.
(In the previous program, we had a timeout for each message)
This time the program will terminate after 5 seconds
*/
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := boring("Joe")
timeout := time.After(5 * time.Second) // Terminate program after 5 seconds.
for {
select {
case s := <-c:
fmt.Println(s)
case <-timeout:
fmt.Println("You're too slow.")
return
}
}
}
func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
这一版本的区别与 Timeout Using Select 的是,定义了一个 timeout。
timeout := time.After(5 * time.Second)
这个 timeout 对于所有的 goroutine 是共用的。
- Quit channel
/*
Quit Channel
You can turn this around and tell Joe to stop when we're tired of listening to him.
*/
package main
import (
"fmt"
"math/rand"
)
func main() {
quit := make(chan bool)
c := boring("Joe", quit)
for i := rand.Intn(10); i >= 0; i-- {
fmt.Println(<-c)
}
quit <- true
fmt.Println("EXIT")
}
func boring(msg string, quit chan bool) <-chan string { // Returns receive-only (<-) channel of strings.
c := make(chan string)
go func() { // Launch the goroutine from inside the function. Function Literal.
for i := 0; ; i++ {
select {
case c <- fmt.Sprintf("%s %d", msg, i):
// Do Nothing
case <-quit:
fmt.Println("Quiting")
return
}
}
}()
return c // Return the channel to the caller.
}