Skip to content

并发 ​


更新于 2023-12-06

关键字 ​

关键字作用
go
chan
select

Goroutine ​

goroutine是用户级别线程
main() 函数是第一个协程
函数之前使用go关键字即创建

Go
func main(){
    login()
    go launch()
}

func main(){
    login()
    go func() { //匿名函数
        launch()
    }()
}

channel ​

协程之间通信的队列

声明 ​

var chanName chan ElementType

Go
ch := make(chan int)      //创建非缓存channel
ch := make(chan int, 10) //创建有缓存的channel

收发数据 ​

channel 之后使用 <-表示发送数据
channel 之前使用 <- 表示接收数据
非缓存channel的读写都是阻塞操作

Go
ch <- x // sends (or write) x through channel ch
x = <-ch // x receives (or reads) data sent to the channel ch
<-ch // receives data, but the result is discarded

使用内置的 close() 函数关闭带有发送功能的channel

Go
close(ch)

关闭channel后,不能再往channel发送消息,否则程序将发生严重错误

Go
func checkAPI(api string, ch chan string) {
    _, err := http.Get(api)
    if err != nil {
        ch <- fmt.Sprintf("ERROR: %s is down!\n", api)
        return
    }

    ch <- fmt.Sprintf("SUCCESS: %s is up and running!\n", api)
}

ch := make(chan string)

for _, api := range apis {
    go checkAPI(api, ch)
}

fmt.Print(<-ch)

无缓冲 channel ​

使用make()函数创建channel时,会默认创建一个无缓冲channel
无缓冲channel在同步发送和接收操作

Go
package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    start := time.Now()

    apis := []string{
        "https://management.azure.com",
        "https://dev.azure.com",
        "https://api.github.com",
        "https://outlook.office.com/",
        "https://api.somewhereintheinternet.com/",
        "https://graph.microsoft.com",
    }
    
    ch := make(chan string)
    
    for _, api := range apis {
        go checkAPI(api, ch)
    }
    
    for i := 0; i < len(apis); i++ {
        fmt.Print(<-ch)
    }
    
    elapsed := time.Since(start)
    fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}

func checkAPI(api string, ch chan string) {
    _, err := http.Get(api)
    if err != nil {
        ch <- fmt.Sprintf("ERROR: %s is down!\n", api)
        return
    }

    ch <- fmt.Sprintf("SUCCESS: %s is up and running!\n", api)
}

输出
ERROR: https://api.somewhereintheinternet.com/ is down!
SUCCESS: https://api.github.com is up and running!
SUCCESS: https://management.azure.com is up and running!
SUCCESS: https://dev.azure.com is up and running!
SUCCESS: https://graph.microsoft.com is up and running!
SUCCESS: https://outlook.office.com/ is up and running!
Done! It took 0.602099714 seconds!

有缓冲 channel ​

当channel的的容量多余1时,channel就是有缓存的channel

Go
ch := make(chan string, 10)

发送数据时数据将被存储到队列中
接收操作将从队列中删除
当channel满时,发送操作将阻塞
当channel空时,读取操作将阻塞

Go
package main

import (
    "fmt"
)

func send(ch chan string, message string) {
    ch <- message
}

func main() {
    size := 4
    ch := make(chan string, size)
    send(ch, "one")
    send(ch, "two")
    send(ch, "three")
    send(ch, "four")
    fmt.Println("All data sent to the channel ...")

    for i := 0; i < size; i++ {
        fmt.Println(<-ch)
    }

    fmt.Println("Done!")
}
Go
func main() {
    size := 2
    ch := make(chan string, size)
    send(ch, "one")
    send(ch, "two")
    go send(ch, "three")
    go send(ch, "four")
    fmt.Println("All data sent to the channel ...")

    for i := 0; i < 4; i++ {
        fmt.Println(<-ch)
    }

    fmt.Println("Done!")
}

Channel 方向 ​

当channel作为参数时,可指定收发方向

Go
chan<- int // 只发送数据
<-chan int // 是接收数据
Go
package main

import "fmt"

func send(ch chan<- string, message string) {
    fmt.Printf("Sending: %#v\n", message)
    ch <- message
}

func read(ch <-chan string) {
    fmt.Printf("Receiving: %#v\n", <-ch)
}

func main() {
    ch := make(chan string, 1)
    send(ch, "Hello World!")
    read(ch)
}

多路复用 ​

select可以同时监听多个channel
当收到多个事件,会随机选择一个处理

Go
package main

import (
    "fmt"
    "time"
)

func process(ch chan string) {
    time.Sleep(3 * time.Second)
    ch <- "Done processing!"
}

func replicate(ch chan string) {
    time.Sleep(1 * time.Second)
    ch <- "Done replicating!"
}

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    go process(ch1)
    go replicate(ch2)

    for i := 0; i < 2; i++ {
        select {
        case process := <-ch1:
            fmt.Println(process)
        case replicate := <-ch2:
            fmt.Println(replicate)
        }
    }
}