Home  >  Article  >  Backend Development  >  How do we guarantee that the canceled context will cause the goroutine to terminate?

How do we guarantee that the canceled context will cause the goroutine to terminate?

PHPz
PHPzforward
2024-02-08 23:54:23927browse

我们如何保证取消的上下文会导致 goroutine 终止?

php editor Zimo will introduce to you how to ensure that canceling the context will cause the goroutine to terminate. When we use goroutine, sometimes we need to cancel it when a certain condition is met to avoid unnecessary calculation and resource waste. To ensure that the goroutine terminates correctly, we can use the mechanism provided by the context package. The context package provides a way to pass requests between goroutines and cancel those requests when needed. By using the context package properly, we can ensure that the goroutine terminates correctly when the context is canceled, thereby avoiding resource leaks and other potential problems. Below we will detail how to use the context package to achieve this goal.

Question content

Assume the following situation occurs:

  • We have the following consumer function, running in a goroutine.

  • Another goroutine is sending integers on the intchan channel without delay. In other words, on each iteration of the for loop, there is a value ready to be received on intchan.

  • Starting consumer The goroutine for goroutine has canceled the context passed to consumer. Therefore, the ctx.done() channel also has a value to receive.

question:

  • In this case, both cases of the select statement are ready to run.
  • According to the go trip, select will randomly select one case since both are ready to run.
  • How to ensure that select will not continue to select <- intchan case? How do we know if <- c​​tx.done() case is ready on every iteration of the for loop, how do we know <- c​​tx.done() Will the case be finally selected?
func consumer(ctx context.context, intchan chan int) {
    for {
        select {
        case <-ctx.done():
            return
        case i := <-intchan:
            foo(i)
        }
    }
}

I try to use the consumer function in the program below. In multiple runs of this program, the consumer and producer goroutines always seem to terminate.

Why don't we end up with a <-ctx.done() run where the case is never executed?

package main

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

func main() {

    ctx, cancelFunc := context.WithCancel(context.Background())

    var wg sync.WaitGroup
    wg.Add(2) // add 2, because we spawn 2 goroutines

    Producer(ctx, &wg)

    fmt.Println(time.Now())
    time.Sleep(time.Second * 5) // cancel the context after 5 seconds

    cancelFunc()
    fmt.Println("CANCELLED")

    wg.Wait() // wait till both producer and consumer goroutines terminate
    fmt.Println(time.Now())

}

func Producer(ctx context.Context, wg *sync.WaitGroup) {
    intChan := make(chan int)

    go Consumer(ctx, intChan, wg)

    go func() {
        defer wg.Done()
        for {
            select {
            case <-ctx.Done():
                return
            case intChan <- 1:
            }
        }
    }()

}

func Consumer(ctx context.Context, intChan chan int, wg *sync.WaitGroup) {
    defer wg.Done()

    for {
        select {
        case <-ctx.Done():
            return
        case _ = <-intChan:
        }
    }
}

Solution

There are no guarantees. The simplest way to guarantee termination is to check for errors using ctx.err() outside the select statement. It's also common to return errors to code passing the context. I would write the consumer function like this:

func Consumer(ctx context.Context, intChan chan int) error {
    for ctx.Err() == nil {
        select {
        case <-ctx.Done():
        case i := <-intChan:
            foo(i)
        }
    }
    return ctx.Err()
}

The above is the detailed content of How do we guarantee that the canceled context will cause the goroutine to terminate?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete