I see some smart people suggesting that what OP is doing is not
correct. I am going to cautiously disagree with some of those replies
and suggest that a similar pattern can be useful for some
applications.
We have an application where we have a similar need. Specifically,
there is a periodic ticker goroutine that calculates the value to send
and there is an IO goroutine that performs i/o with the latest value
(and that i/o could block for an unknown amount of time. It is OK to
drop values that are not the most recent values, but we would like to
have the i/o happen as quickly as possible after the calculation. As
an example, imagine an application that cares about soft realtime
responsiveness like telephony or multiplayer gaming (those
applications would probably be OK using UDP for example).
For a long time we used some shared memory for the value protected by
a mutex. Then the signaling would be performed using a condition
variable. That worked OK, but we also wanted to have a way to queue a
value if i/o is in progress and have a second round of i/o start
immediately after the first one returns so we needed another indicator
for that. We ended up using a pointer to the value where the nil
pointer could represent that no new value is waiting. We ran with
that code for years and it worked, but was a little subtle and hard to
follow.
Eventually I rewatched Bryan Mill's excellent "Rethinking Classical
Concurrency" talk (
https://www.youtube.com/watch?v=5zXAHh5tJqQ) and
tried to think about how to apply some of those ideas to the problem
instead of using the condition variable. My initial solution was
similar to some of the ideas in the talk and have 2 channels: 1 as a
semaphore to control the "direction" of the flow and the other channel
with the value. Thinking about it more, the thing being sent over the
"semaphore" channel could actually be the value itself as long as only
one goroutine is doing the sending. That simplifies the design down
to a single channel. The pattern we ended up looks like this:
https://play.golang.org/p/m6ONLGZ7nU2
There are a few caveats with that solution:
- Only one goroutine can do the triggering and sending on the channel
(if you wanted to have multiple sending goroutines, then I think the
multiple channel semaphore pattern can work).
- With a single channel (and single goroutine trigger) solution, the
buffered channel should be drained unconditionally by the triggering
goroutine in a non-blocking way before each send of a new value.
- There is a logical race (not a data race) between the triggering
goroutine and the i/o goroutine and the values that actually get sent
will be subject to the i/o delays and goroutine scheduler. Your
application must be OK with that (that was acceptable for our
application).
Best,
Tarmigan
> To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/CAOyqgcUjEPj3%2BWFpnBsgiPvKMZqbpNn%3DM1-eN6m6B4bXtvRp7A%40mail.gmail.com.