golang频道用于什么?

Pet*_*leg 21 channel go

在查看一些golang代码时,我发现了以下内容:

  ch := make(chan int)
Run Code Online (Sandbox Code Playgroud)

我在golang Chanels的在线教程中查了解:

https://tour.golang.org/concurrency/2

但我觉得这个例子不清楚.

有人可以给我一个简单的解释和使用频道的例子吗?

shi*_*ngh 47

chan是Golang的一个频道.简单来说,你可以认为它是一个盒子,你把一个项目放在一端,然后从另一端挑选它.(顺序没关系)类似的东西

无缓冲的频道

在此输入图像描述

缓冲频道

在此输入图像描述

这是我为您理解频道而编写的小代码.现在改变go例程的顺序并查看输出.每次输出可能不同.

    package main

    import (
        "fmt"
        "time"
    )

    func main() {
        messages := make(chan int)
        go func() {
            time.Sleep(time.Second * 3)
            messages <- 1
        }()
        go func() {
            time.Sleep(time.Second * 2)
            messages <- 2
        }() 
        go func() {
            time.Sleep(time.Second * 1)
            messages <- 3
        }()
        go func() {
            for i := range messages {
                fmt.Println(i)
            }
        }()
        go func() {
            time.Sleep(time.Second * 1)
            messages <- 4
        }()
        go func() {
            time.Sleep(time.Second * 1)
            messages <- 5
        }()
        time.Sleep(time.Second * 5)
    }
Run Code Online (Sandbox Code Playgroud)

为了更好地理解,请访问此博客,其中在GUI中描述了例程和通道.

访问http://divan.github.io/posts/go_concurrency_visualize/

  • 我喜欢插图! (10认同)
  • 这是巨大的帮助!非常感谢你 (3认同)
  • _“顺序无关紧要” _ –这是不正确的。Go规范指出:_“通道充当先进先出队列。” _ (2认同)

icz*_*cza 41

我认为这个规格非常明确.规格:渠道类型:

通道提供了一种机制,用于通过发送接收指定元素类型的值来同时执行通信功能.

当您有多个同时执行的goroutine时,通道提供了允许goroutine相互通信的最简单方法.

一种通信方式是通过两个goroutine可见的"共享"变量,但这需要适当的锁定/同步访问.

相反,Go赞成频道.引自有效围棋:通过沟通分享:

不要通过共享内存进行通信; 相反,通过沟通分享记忆.

因此,不是将消息放入共享切片,例如,您可以创建一个通道(两个goroutine都可见),并且没有任何外部同步/锁定,一个goroutine可以通过通道发送消息(值),另一个goroutine可以接收他们.

在任何给定时间,只有一个goroutine可以访问该值.根据设计,数据竞争不会发生.

所以事实上,任何数量的goroutine都可以在同一个通道上发送值,任何数量的goroutine都可以从中接收值,仍然没有任何进一步的同步.有关详细信息,请参阅相关问题:如果我正确使用频道,是否需要使用互斥锁?

频道示例

让我们看一个例子,我们为并发计算目的开始另外两个goroutine.我们将一个数字传递给第一个数字,它会增加1,并在第二个通道上传递结果.第二个goroutine将收到一个数字,乘以10并将其传递到结果通道:

func AddOne(ch chan<- int, i int) {
    i++
    ch <- i
}

func MulBy10(ch <-chan int, resch chan<- int) {
    i := <-ch
    i *= 10
    resch <- i
}
Run Code Online (Sandbox Code Playgroud)

这是如何调用/使用它:

func main() {
    ch := make(chan int)
    resch := make(chan int)

    go AddOne(ch, 9)
    go MulBy10(ch, resch)

    result := <-resch
    fmt.Println("Result:", result)
}
Run Code Online (Sandbox Code Playgroud)

通过渠道进行沟通也会照顾到彼此等待的goroutine.在此示例中,它表示MulBy10()将等待直到AddOne()递送递增的数字,并将在打印结果之前main()等待MulBy10().按预期输出(在Go Playground上试试):

Result: 100
Run Code Online (Sandbox Code Playgroud)

语言支持

有许多语言结构可以方便地使用频道,例如:

  • for ... range一个通道上循环通过从一个信道接收的值,直到该信道被关闭.
  • select语句可用于列出多个通道操作,例如在通道上发送通道接收,并且将选择可以无阻塞地进行的通道操作(如果有多个可以继续,则随机选择;如果没有,则将阻止准备).
  • 接收运算符有一种特殊形式,允许您检查通道是否已关闭(除了接收值): v, ok := <-ch
  • 内置len()函数告诉排队的元素数(未读); 构建cap()函数返回通道缓冲区容量.

其他用途

有关更实际的示例,请参阅如何使用通道实现工作池.类似的用途是将值从生产者分配给消费者.

另一个实际示例是使用缓冲通道实现内存池.

另一个实际的例子是经纪人的优雅实施.

通道通常用于超时某些阻塞操作,利用返回的通道time.After(),在指定的延迟/持续时间后"触发"("触发"表示将在其上发送值).请参阅此示例以进行演示(在Go Playground上尝试):

ch := make(chan int)

select {
case i := <-ch:
    fmt.Println("Received:", i)
case <-time.After(time.Second):
    fmt.Println("Timeout, no value received")
}
Run Code Online (Sandbox Code Playgroud)

它可用于等待某个值的最大时间量,但如果其他goroutine无法提供该值,我们可能会决定做其他事情.

另外一种特殊形式的通信可能只是表示某些操作的完成(没有实际发送任何"有用的"数据).这种情况可以由具有任何元素类型的信道实现,例如chan int,并且在其上发送任何值,例如0.但由于发送的值不包含任何信息,您可以声明它chan struct{}.或者甚至更好,如果你只需要一次性信令,你可以关闭可以在另一侧使用for ... range或从中接收的信道(当从一个封闭的信道接收时立即进行,产生零值)元素类型).还要知道即使某个信道可以用于这种信令,也有更好的选择:sync.WaitGroup.

进一步阅读

值得了解通道公理以避免出现令人惊讶的行为:非初始化通道如何表现?

Go Blog:通过沟通分享记忆

Go Blog:Go Concurrency Patterns:Pipelines and cancellation

Go Blog:Advanced Go Concurrency Patterns

  • 我喜欢这个答案,尤其是有关使用time.After(time.Second)的内容。我不知道可以做到这一点。 (3认同)

归档时间:

查看次数:

9427 次

最近记录:

6 年 前