16 Go Concurrency Patterns: Timing out, moving on

Go Concurrency Patterns: Timing out, moving on

23 September 2010

Concurrent programming has its own idioms. A good example is timeouts. Although Go‘s channels do not support them directly, they are easy to implement. Say we want to receive from the channel ch, but want to wait at most one second for the value to arrive. We would start by creating a signalling channel and launching a goroutine that sleeps before sending on the channel:

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

We can then use a select statement to receive from either ch or timeout. If nothing arrives on ch after one second, the timeout case is selected and the attempt to read from ch is abandoned.

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

The timeout channel is buffered with space for 1 value, allowing the timeout goroutine to send to the channel and then exit. The goroutine doesn‘t know (or care) whether the value is received. This means the goroutine won‘t hang around forever if the ch receive happens before the timeout is reached. The timeout channel will eventually be deallocated by the garbage collector.

(In this example we used time.Sleep to demonstrate the mechanics of goroutines and channels. In real programs you should use ` time.After`, a function that returns a channel and sends on that channel after the specified duration.)

Let‘s look at another variation of this pattern. In this example we have a program that reads from multiple replicated databases simultaneously. The program needs only one of the answers, and it should accept the answer that arrives first.

The function Query takes a slice of database connections and a query string. It queries each of the databases in parallel and returns the first response it receives:

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
}

In this example, the closure does a non-blocking send, which it achieves by using the send operation in selectstatement with a default case. If the send cannot go through immediately the default case will be selected. Making the send non-blocking guarantees that none of the goroutines launched in the loop will hang around. However, if the result arrives before the main function has made it to the receive, the send could fail since no one is ready.

This problem is a textbook example of what is known as a race condition, but the fix is trivial. We just make sure to buffer the channel ch (by adding the buffer length as the second argument to make), guaranteeing that the first send has a place to put the value. This ensures the send will always succeed, and the first value to arrive will be retrieved regardless of the order of execution.

These two examples demonstrate the simplicity with which Go can express complex interactions between goroutines.

By Andrew Gerrand

Related articles

原文地址:https://www.cnblogs.com/kaid/p/9698479.html

时间: 2024-08-29 10:52:46

16 Go Concurrency Patterns: Timing out, moving on的相关文章

18 A GIF decoder: an exercise in Go interfaces

A GIF decoder: an exercise in Go interfaces 25 May 2011 Introduction At the Google I/O conference in San Francisco on May 10, 2011, we announced that the Go language is now available on Google App Engine. Go is the first language to be made available

19 Error handling and Go

Error handling and Go 12 July 2011 Introduction If you have written any Go code you have probably encountered the built-in error type. Go code uses error values to indicate an abnormal state. For example, the os.Openfunction returns a non-nil error v

13 JSON-RPC: a tale of interfaces

JSON-RPC: a tale of interfaces 27 April 2010 Here we present an example where Go's interfaces made it easy to refactor some existing code to make it more flexible and extensible. Originally, the standard library's RPC package used a custom wire forma

22 Gobs of data

Gobs of data 24 March 2011 Introduction To transmit a data structure across a network or to store it in a file, it must be encoded and then decoded again. There are many encodings available, of course: JSON, XML, Google's protocol buffers, and more.

17 Go Slices: usage and internals

Go Slices: usage and internals 5 January 2011 Introduction Go's slice type provides a convenient and efficient means of working with sequences of typed data. Slices are analogous to arrays in other languages, but have some unusual properties. This ar

21 JSON and Go

JSON and Go 25 January 2011 Introduction JSON (JavaScript Object Notation) is a simple data interchange format. Syntactically it resembles the objects and lists of JavaScript. It is most commonly used for communication between web back-ends and JavaS

《Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects》Vol.2 笔记

GoF的23种经典模式使得设计模式开始成为程序员的通用语言.<Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects> Volume 2主要总结了并行系统中四大类16种设计模式.这里是一个摘录整理. Service Access and Configuration Wrapper Facade 它将底层的系统级API包装成加面向对象的,统一的和可移植的接口.像Thread, Mu

Golang常见误区(二)

35. 关闭 HTTP 的响应体 使用 HTTP 标准库发起请求.获取响应时,即使你不从响应中读取任何数据或响应为空,都需要手动关闭响应体.新手很容易忘记手动关闭,或者写在了错误的位置: // 请求失败造成 panic func main() { resp, err := http.Get("https://api.ipify.org?format=json") defer resp.Body.Close()    // resp 可能为 nil,不能读取 Body if err !=

GO 新开发者要注意的陷阱和常见错误

转自:http://colobu.com/2015/09/07/gotchas-and-common-mistakes-in-go-golang/ 初级 开大括号不能放在单独的一行 未使用的变量 未使用的Imports 简式的变量声明仅可以在函数内部使用 使用简式声明重复声明变量 偶然的变量隐藏Accidental Variable Shadowing 不使用显式类型,无法使用"nil"来初始化变量 使用"nil" Slices and Maps Map的容量 字符