Multiple goroutines waiting for event

2,702 views
Skip to first unread message

Miki Tebeka

unread,
Sep 19, 2011, 11:43:42 PM9/19/11
to golan...@googlegroups.com
Greetings,

I want to have several goroutines wait for an event. There are several ways to do it - wait on a channel, sync.WaitGroup ...
Is there a "preferred" way?

Thanks,
--
Miki

Dmitry Vyukov

unread,
Sep 19, 2011, 11:53:28 PM9/19/11
to golan...@googlegroups.com
I think the "default" way is to use channels. If you can express it with channels and are satisfied with the result, then you are done. If not, then it depends on details.


Robert Bloomquist

unread,
Sep 20, 2011, 3:01:20 AM9/20/11
to golan...@googlegroups.com
Multiple goroutines waiting for an event sounds like a job for sync.Cond to me, but it really depends on what exactly is going on.  Channels work, but you'd have to send the signal as many times as you have goroutines waiting, which seems kind of inefficient to me, but I'm not all that familiar with the implementation details of sync.WaitGroup or sync.Cond.

roger peppe

unread,
Sep 20, 2011, 3:24:38 AM9/20/11
to golan...@googlegroups.com
yes, sync.Cond can be a better fit than channels for this kind of problem.

but if the event happens only once, then closing a channel works well too,
because an indeterminate number of reading goroutines can
all be woken with that single action.

one advantage of using a channel is that it is easily added to
a select with other channels where using sync.Cond would require an
extra waiting goroutine.

On 20 September 2011 08:01, Robert Bloomquist

Kyle Lemons

unread,
Sep 20, 2011, 1:01:09 PM9/20/11
to roger peppe, golan...@googlegroups.com
I highly recommend using channels for this.  Lock-based "broadcast" messages are very difficult to reason about, sync.Cond doubly so---for instance, the following does not work as one might expect:
        l := new(sync.Mutex)
        c := sync.NewCond(l)
        for i := 0; i < 10; i++ {
                go func() {
                        l.Lock()
                        defer l.Unlock()
                        c.Wait()
                        fmt.Println(i)
                }()
        }
        c.Broadcast()

Whereas the following works fine:
        c := make(chan bool)
        for i := 0; i < 10; i++ {
                go func() {
                        <-c
                        fmt.Println(i)
                }()
        }
        close(c)

Miki Tebeka

unread,
Sep 20, 2011, 1:08:54 PM9/20/11
to golan...@googlegroups.com

but if the event happens only once, then closing a channel works well too,

Nice, will use that one. Thanks!

Kevin Ballard

unread,
Sep 21, 2011, 4:54:52 PM9/21/11
to Kyle Lemons, roger peppe, golan...@googlegroups.com
On Tue, Sep 20, 2011 at 10:01 AM, Kyle Lemons <kev...@google.com> wrote:
I highly recommend using channels for this.  Lock-based "broadcast" messages are very difficult to reason about, sync.Cond doubly so---for instance, the following does not work as one might expect:
        l := new(sync.Mutex)
        c := sync.NewCond(l)
        for i := 0; i < 10; i++ {
                go func() {
                        l.Lock()
                        defer l.Unlock()
                        c.Wait()
                        fmt.Println(i)
                }()
        }
        c.Broadcast()

Am I correct in believing that this may fail because the call to c.Broadcast() may happen before all goroutines have executed c.Wait()?

Kyle Lemons

unread,
Sep 21, 2011, 5:28:17 PM9/21/11
to Kevin Ballard, roger peppe, golan...@googlegroups.com
I highly recommend using channels for this.  Lock-based "broadcast" messages are very difficult to reason about, sync.Cond doubly so---for instance, the following does not work as one might expect:
        l := new(sync.Mutex)
        c := sync.NewCond(l)
        for i := 0; i < 10; i++ {
                go func() {
                        l.Lock()
                        defer l.Unlock()
                        c.Wait()
                        fmt.Println(i)
                }()
        }
        c.Broadcast()

Am I correct in believing that this may fail because the call to c.Broadcast() may happen before all goroutines have executed c.Wait()

Precisely.  The difference is that close is persistent, broadcast is not.  I have actually seen waitgroup used in reverse of its normal way (e.g. Wait() in all of the clients, Add() in the source), but I think the channel send is clearer.
~K
Reply all
Reply to author
Forward
0 new messages