golang学习笔记——将 channel 用作通信机制

将 channel 用作通信机制

golang学习笔记——将 channel 用作通信机制
golang学习笔记——并发计算斐波纳契数

Go 中的 channel 是 goroutine 之间的通信机制。 请记住 Go 的并发方法是:不是通过共享内存通信;而是通过通信共享内存。当你需要将值从一个 goroutine 发送到另一个时,可以使用通道。 让我们看看它们的工作原理,以及如何开始使用它们来编写并发 Go 程序。

Channel 语法

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

关闭 channel

close(ch)

无缓冲 channel

使用 make() 函数创建 channel 时,会创建一个无缓冲 channel,这是默认行为。 无缓冲 channel 会阻止发送操作,直到有人准备好接收数据。

package main

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

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

    apis := []string{
        "https://mp.csdn.net/",
        "https://dev.azure.com",
        "https://api.somewhereintheinternet.com/",
        "https://gitcode.net/",
    }

    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)
}

缓冲 channels

下面是一个理解有缓冲 channel 的简单示例

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!")
}

输出

All data sent to the channel ...
one
two
three
four
Done!

试着将size改为2
重新运行程序时,将看到以下错误:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.send(...)
        D:/golang2023/main.go:8
main.main()
        D:/golang2023/main.go:16 +0x97
exit status 2

channel 与 goroutine

channel 与 goroutine 有着紧密的联系。 如果没有另一个 goroutine 从 channel 接收数据,则整个程序可能会永久处于被阻止状态。 正如你所见,这种情况确实会发生。

缓冲 channels 示例

使用之前用于检查 API 的示例,并创建大小为 10 的缓冲通道

package main

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

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

    apis := []string{
        "https://management.azure.com",
        "https://dev.azure.com",
        "https://mp.csdn.net/",
        "https://outlook.office.com/",
        "https://api.somewhereintheinternet.com/",
        "https://gitcode.net/",
    }

    ch := make(chan string, 10)

    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)
}

多路复用

最后,让我们讨论如何使用 select 关键字与多个通道同时交互。 有时,在使用多个 channel 时,需要等待事件发生。 例如,当程序正在处理的数据中出现异常时,可以包含一些逻辑来取消操作。

select 语句的工作方式类似于 switch 语句,但它适用于 channel。 它会阻止程序的执行,直到它收到要处理的事件。 如果它收到多个事件,则会随机选择一个。

select 语句的一个重要方面是,它在处理事件后完成执行。 如果要等待更多事件发生,则可能需要使用循环。

让我们使用以下程序来看看 select 的运行情况:

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)
        }
    }
}

输出

Done replicating!
Done processing!

请注意,replicate 函数首先完成,这就是首先在终端中看到其输出的原因。 main 函数存在一个循环,因为 select 语句在收到事件后立即结束,但我们仍在等待 process 函数完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值