Go 并发范式 超时

举报
张俭 发表于 2023/12/30 15:23:43 2023/12/30
【摘要】 翻译自https://blog.golang.org/concurrency-timeouts Go并发范式:超时,继续执行并发编程有自己的习惯用法。 超时是一个很好的例子。在商用软件开发时,所有操作都需要有超时。虽然 Go 的channel不直接支持超时,但很容易实现。假设我们想从通道 ch 接收,但希望实现一秒钟超时。 我们可以创建一个信号channel并启动一个在通道上发送之前休眠的...

翻译自

https://blog.golang.org/concurrency-timeouts

Go并发范式:超时,继续执行

并发编程有自己的习惯用法。 超时是一个很好的例子。在商用软件开发时,所有操作都需要有超时。

虽然 Go 的channel不直接支持超时,但很容易实现。假设我们想从通道 ch 接收,但希望实现一秒钟超时。 我们可以创建一个信号channel并启动一个在通道上发送之前休眠的 goroutine

timeout := make(chan bool, 1)
go func() {
    time.Sleep(1 * time.Second)
    timeout <- true
}()

我们可以使用select语句从ch或者timeout中接收。如果过了1秒还没有数据返回,超时的case会被选中,尝试从ch读取的操作被放弃

select {
case <-ch:
    // a read from ch has occurred
case <-timeout:
    // the read from ch has timed out
}

timeout channel有1个buffer,允许超时 goroutine 发送到通道然后退出。 goroutine 不关心这个值是否被接收了。这意味着如果 ch 接收发生在超时之前,goroutine 不会永远挂起。timeout channel 最终会被gc释放。

(在这个例子中,我们使用 time.Sleep 来演示 goroutines 和通道的机制。在实际程序中,你应该使用 time.After来完成这个延迟发送)

让我们看看这种模式的另一种变体。在这个例子中,我们有一个程序可以同时从多个数据库的副本中读取数据。程序只需要一个结果,它应该接受最先返回的结果。

函数 Query 接受多个数据库连接和一个查询字符串。它并行查询每个数据库并返回它收到的第一个响应:

func Query(conns []Conn, query string) Result {
    ch := make(chan Result)
    for _, conn := range conns {
        go func(c Conn) {
            select {
            case ch <- c.DoQuery(query):
            default:
            }
        }(conn)
    }
    return <-ch
}

在这个例子中,闭包执行非阻塞发送,它通过在把send放在带有defaultselect中实现。 如果send不能立即完成,则将选择default情况。 非阻塞发送保证循环中启动的任何 goroutine 都不会挂起。

但是,这个例子中有一个问题,如果结果在主函数执行到11行接收的时候之前到达,发送都会失败,最终函数无法取得结果。

这个问题是所谓的竞争条件的教科书示例,修复非常简单。 我们只要保证channel有着缓冲通道(通过添加缓冲区长度作为 make 的第二个参数)来保证第一次发送有一个放置值的地方。 这确保发送总是成功,并且无论执行顺序如何,第一个值都会被获取。

这两个例子展示了 Go 可以简单地表达 goroutines 之间复杂的交互。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。