-2

I'm writing a function that needs to run and finish as fast as possible.

It needs to make 3 REST calls and should any of these return a bad result, it needs to exit.

Each of the REST calls are being made in their own go routines and return the results to the main thread via a buffered channel.

Because I'm using buffered channels I know that the sending threads will send the results of the REST request via the buffered channel and exit - no possibility of a goroutine leak.

My question is; lets say I get the response from the first REST requests and it's a bad result (by which I mean the function as a whole needs exit), is it OK for me close the other two channels and exit without reading the contents of the other 2 buffered channels?

I have a feeling this isn't recommended and if that's they case why so?

  • An alternate approach is to use an [errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) instead of channels. The [parallel example](https://pkg.go.dev/golang.org/x/sync/errgroup#example-Group-Parallel) is a good starting point for your use case. The advantage of this approach is that the context is canceled when any of the REST APIs returns an error. –  Jul 17 '22 at 02:18
  • "is it OK for me close the other two channels" - Sending to a closed channel (buffered or not) will lead to a `panic: send on closed channel`. It's not really clear how you are using the channels (you say "**a** buffered channel" then "buffered channels" then "other two channels") so some code would help. [This question](https://stackoverflow.com/q/8593645/11810946) might also prove of use. – Brits Jul 17 '22 at 03:13

1 Answers1

0

You shouldn't close a channel which is going to be written by another goroutine. The usual pattern is to have the writer to close the channel it's writing in when it's done. If you want to cancel the call of a goroutine you should use a context.Context instead. Here is a sample synchronisation code between to goroutine using a Context to cancel another one.

package main

import (
    "context"
    "fmt"
    "time"
)

func f(ctx context.Context, ch chan<- struct{}) {
    select {
    case <-time.After(time.Hour):
        fmt.Println("sending data on the channel")
        ch <- struct{}{}
    case <-ctx.Done():
        fmt.Println("closing channel")
        close(ch)
    }
}

func main() {
    ch := make(chan struct{})
    ctx, cancel := context.WithCancel(context.Background())
    go f(ctx, ch)
    cancel()
    <-ch
}
davidriod
  • 937
  • 5
  • 14