do i need to wait all child routine exit

153 views
Skip to first unread message

dkx...@gmail.com

unread,
Oct 25, 2016, 8:29:29 AM10/25/16
to golang-nuts
in my case, i have serval child routine for query for dns record, but i just need a answer

code looks like:
    recvChan := make(chan *dnsRecord, 1)

    defer close(recvChan)



    for _, server := range DnsServers {

        go doResolve(server, req, recvChan)   // query ip address

    }



    select {

    case r := <-recvChan:   // receive address

        responseRecord(w, req, r)  // response to client

    case <-time.After(2 * time.Second):  // timeout

        break

    }
     
   
return


do i need to wait all child routine exit before return

Ken MacDonald

unread,
Oct 25, 2016, 8:53:59 AM10/25/16
to dkx...@gmail.com, golang-nuts
You should not need to wait for more than a single response if any of the responses will do the job. I've done this querying several distributed DBs for an answer, and just taking the one that comes back fastest.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Nick Patavalis

unread,
Oct 25, 2016, 10:09:00 AM10/25/16
to golang-nuts
Hi,

You don't need to wait for the goroutines to exit, per se, but, as far as I can tell, your code will panic when it "return"s and one of the still-running resolver goroutines try to send to the closed channel.

One solution would be not to close the channel upon return. The channel will be collected when the last goroutine exits. You must, though, make sure that the resolver goroutines will not block sending on recvChann. Use something like this:
 
  select {
 
case: recvChan <- r
 
default:
 
}


This would work, but it feels a bit dirty: you leave goroutines running while you no longer need them. It would be better if you could make your resolver goroutines cancel-able:


 recvChan
:= make(chan *dnsRecord, 1)

 quitChan
:= make(chan struct{})
 defer close
(quitChan)


 
for _, server := range DnsServers {

        go doResolve
(server, req, recvChan, quitChan)   // query ip address

 
}

 
select {
     
case r := <-recvChan:   // receive address
         responseRecord
(w, req, r)  // response to client
     
case <-time.After(2 * time.Second):  // timeout
 
}


 
// chanQuit will be closed    
 
return
 

Then you must make your resolvers quit when quitChannel is closed; depending on what your resolvers do, it may be complicated. It is arguable whether this extra complication is worth the gain.

(you could also wait for the resolvers to stop after canceling them, but I see no practical advantage in doing so...)


Axel Wagner

unread,
Oct 25, 2016, 10:17:10 AM10/25/16
to golang-nuts, dkx...@gmail.com
I suggest using errgroup (and/or context.Context in general) instead of rolling your own cancellation and timeout logic. It should make it pretty much trivial to kick off all the queries and cancel the context once you got the first result. It will also allow you to propagate that cancellation seamlessly through the ecosystem (e.g. cancellation traverses network boundaries when used with grpc and it will kill child processes if they are spawned with a context). The errgroup documentation has specific examples for how to do this kind of thing.

I would, in any case, encourage you to use cancellation and not simply let the goroutines run, because that's just wasteful and might be incorrect. In particular, if they are writing to a channel, make sure that they don't block because there is no reader, which would leak resources. You should either wait for them, or make sure that they'll eventually die.

Nick Patavalis

unread,
Oct 25, 2016, 10:24:05 AM10/25/16
to golang-nuts, dkx...@gmail.com


On Tuesday, October 25, 2016 at 5:17:10 PM UTC+3, Axel Wagner wrote:
I suggest using errgroup (and/or context.Context in general) instead of rolling your own cancellation and timeout logic. It should make it pretty much trivial to kick off all the queries and cancel the context once you got the first result. It will also allow you to propagate that cancellation seamlessly through the ecosystem (e.g. cancellation traverses network boundaries when used with grpc and it will kill child processes if they are spawned with a context). The errgroup documentation has specific examples for how to do this kind of thing.

Yes using context.Context for cancelation would be even better.
  

Xu Lei

unread,
Nov 25, 2016, 3:16:09 AM11/25/16
to golang-nuts
thanks for replying!!!

the answers are great
Reply all
Reply to author
Forward
0 new messages