How does this go-routine in an anonymous function exactly work?

Issue

func (s *server) send(m *message) error {
    go func() {
        s.outgoingMessageChan <- message
    }()
    return nil
}

func main(s *server) {
    for {
        select {
        case <-someChannel:
            // do something
        case msg := <-s.outGoingMessageChan:
            // take message sent from "send" and do something
        }
    }
}

I am pulling out of this s.outgoingMessageChan in another function, before using an anonymous go function, a call to this function would usually block – meaning whenever send is called, s.outgoingMessageChan <- message would block until something is pulling out of it. However after wrapping it like this it doesn’t seem to block anymore. I understand that it kind of sends this operation to the background and proceeds as usual, but I’m not able to wrap my head around how this doesn’t affect the current function call.

Solution

Each time send is called a new goroutine is created, and returns immediately. (BTW there is no reason to return an error if there can never be an error.) The goroutine (which has it’s own "thread" of execution) will block if nothing is ready to read from the chan (assuming it’s unbuffered). Once the message is read off the chan the goroutine will continue but since it does nothing else it will simply end.

I should point out that there is no such thing as an anonymous goroutine. Goroutines have no identifier at all (except for a number that you should only use for debugging purposes). You have an anonymous function which you put the go keyword in front causing it to run in a separate goroutine.

For a send function that blocks as you seem to want then just use:

func (s *server) send(m *message) {
    s.outgoingMessageChan <- message
}

However, I can’t see any point in this function (though it would be inlined and just as efficient as not using a function).

I suspect you may be calling send many times before anything is read from the chan. In this case many new goroutines will be created (each time you call send) which will all block. Each time the chan is read from one will unblock delivering its value and that goroutine will terminate. Doing this you are simply creating an inefficient buffering mechanism. Moreover, if send is called for a prolonged period at a faster rate than the values can be read from the chan then you will eventually run out of memory. Better would be to use a buffered chan (and no goroutines) that once it (the chan) became full exerted "back-pressure" on whatever was producing the messages.

Another point is that the function name main is used to identify the entry point to a program. Please use another name for your 2nd function above. It also seems like it should be a method (using s *server receiver) than a function.

Answered By – AJR

Answer Checked By – Marie Seifert (GoLangFix Admin)

Leave a Reply

Your email address will not be published.