Reputation: 33
Often when writing concurrent programs with multiple goroutines, those goroutines require access to a client, for instance writing a REST API where each HTTP handler needs to use a single initialized Redis client to read and write to the Redis instance.
I either have a client instance with a mutex lock so only one goroutine can use it at any one time OR have a client goroutine which the other goroutines can request a read via a channel. Both ways work but I am wondering which is more idiomatic? Thanks for your help
Upvotes: 3
Views: 1180
Reputation:
If you have only one client, and only simple operations to perform on it, use a mutex. It's typically simple and easy to understand, unlike a goroutine with a bunch of channels and a select statement. Be sure to encapsulate the thread-safety in order not to burden the API user with locks.
Compare:
var (
mutex sync.Mutex
resource = 0
)
func Inc() int {
mutex.Lock()
defer mutex.Unlock()
resource++
return resource
}
With:
var requests = make(chan chan int)
func init() {
go func() {
resource := 0
for {
response := <- requests
resource++
response <- resource
}
}()
}
func Inc() int {
response := make(chan int)
requests <- response
return <-response
}
The former is clearly more concise and maintainable. And especially if the resource isn't global, the channel approach also requires manual management of goroutines since goroutines aren't garbage-collected (see how to stop a goroutine).
If you don't mind having more than one client, use a pool of clients. go-redis supports pooling out of the box. The pool itself is thread-safe and can be used to acquire one of the idle connections.
Upvotes: 2