When I write codes for ik, often I find it necessary to synchronize the access to some resource from a number of goroutines. While using channels almost looks like the way to “go” , simply applying mutexes also has some clear benefits. I think it is a good time to do some comparison over the two, so here it goes:
Good-old mutexes
Procedures that manipulate on the shared resource are surrounded by mutex calls. It should be easy to follow the control flow because the callgraph from the initiator of the action to the critical sections is pretty much straight-forward, and so it is to do the function-level testing as everything is serialized in a single flow and all you need is call the target function.
But it'd generally turn out to be a nightmare to find out the cause of the deadlock where three or more locks are involved.
Daemon pattern
Access to the resource is restricted to just one goroutine and every initiator of the action is required to send the message to the goroutine to let it manipulate the resource. The result of the manipulation is retrieved through the channel too. This is what I call “daemon pattern” as the manipulating goroutine behaves analogously to Unix's service daemons. I don't know the exact name of the pattern but there should be a good name for it.
One of the notable benefit over mutexes is that you can identify what causes a deadlock much easier because synchronization happens only when the communication takes place. In addition, it enables you to implement timeout with another channel without much difficulty.
But you'd end up managing the lifetime of the daemon goroutine as it needs to be restarted when it aborts for some reasons and needs to be stopped when it is no longer necessary. Besides it'd be harder to figure out the control flow because of the separated callgraphs and extra codes for the communication. Thus the code for function-level testing would be complicated.
Summary
-
Mutexes
-
Pros
- Straight-forward, top-down callgraph
- Easily testable in a single thread
-
Cons
- Terribly hard to find out the deadlock site
-
-
Daemon pattern
-
Pros
- Comparatively easier to find deadlocks because locks are far more obvious than mutexes
- Need to manage lifetime of daemon goroutines
-
Cons:
- Testing would be a little bit difficult
-
P.S. Go wiki has the dedicated page for this topic: Mutex or Channel