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.