lnshi
lnshi

Reputation: 2898

How to use channel to notify a goroutine to quit properly

Note: I searched this topic with google, and read nearly everything I can find, but still cannot get a proper/reasonable/production ready answer.

Basically all answers are similar, just like this one: how to stop a groutine, all in same pattern, no exception: the real work is fmt.Println(1) to print something, or simply be // Do other stuff,

But if keep the real work at for select default case branch, then it will be executed multiple times, for printing something it is fine, but clearly it is not ready for real work.

The only valid approach I can imagine is put the real work on a case branch then send only one signal to that case to notify it to start, like here: playground, but just feel wried, also with this approach, does it create some issues potentially?

Added code fragments to show exactly what i am trying to achieve: https://play.golang.org/p/7xjjiW7XdoQ, i want to achieve when client close connection, then immediately terminate my handler, release the resources, and quit unconditionally.

Upvotes: 0

Views: 2822

Answers (1)

ain
ain

Reputation: 22759

Instead of using for select loop you want to check for the cancel signal in multiple places. But in order to not block you still have to use select (with empty default case) which makes it a bit akward. I'm using the context.Context instead of "plain cancel channel" but the idea is the same:

func doWork(ctx context.Context) {
    fmt.Println("Doing some work 1")
    time.Sleep(time.Second * 5)
    // check is the task cancelled
    select {
    case <-ctx.Done():
        fmt.Println("cancelled at checkpoint 1")
        return
    default:
    }
    fmt.Println("Doing some work 2")
    time.Sleep(time.Second * 5)
    // check is the task cancelled
    select {
    case <-ctx.Done():
        fmt.Println("cancelled at checkpoint 2")
        return
    default:
    }
    fmt.Println("Doing some work 3")
    time.Sleep(time.Second * 5)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    go func() {
        doWork(ctx)
        wg.Done()
    }()
    wg.Wait()
    fmt.Println("done")
}

Upvotes: 1

Related Questions