ctx超时时,如何完全终止正在运行的go func()?

When I want ctx timeout, what should I do to completely terminate the method that is executing longRunningCalculation()?

package main

import (
    "context"
    "log"
    "time"
)

func longRunningCalculation(timeCost int) chan string {
    result := make(chan string)
    go func() {
        time.Sleep(time.Second * (time.Duration(timeCost)))
        log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
        result <- "Done"
        log.Println(timeCost)
    }()
    return result
}
func jobWithTimeout() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    select {
    case <-ctx.Done():
        log.Println(ctx.Err())
        return
    case result := <-longRunningCalculation(3):
        log.Println(result)
    }
}

func main() {
    jobWithTimeout()
    time.Sleep(time.Second * 5)
}

What did you expect to see? 2019/09/25 11:00:16 context deadline exceeded

What did you see instead? 2019/09/25 11:00:16 context deadline exceeded 2019/09/25 11:00:17 Still doing other things...

To stop the goroutine started by longRunningCalculation when the caller's context times out, you need to pass ctx into longRunningCalculation and explicitly handle the context timing out, the same way you do in jobWithTimeout

Doing things that way also means instead of calling time.Sleep, that time.Tick will be a better choice, so both timers are running at the same time. Like so:

package main

import (
    "context"
    "log"
    "time"
)

func longRunningCalculation(ctx context.Context, timeCost int) chan string {
    result := make(chan string)
    go func() {
        calcDone := time.Tick(time.Second * time.Duration(timeCost))
        log.Printf("entering select (longRunningCalculation)")
        select {
        case <-ctx.Done():
            result <- "Caller timed out"
            return
        case <-calcDone:
            log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
            result <- "Done"
        }
        log.Println(timeCost)
    }()
    return result
}

func jobWithTimeout() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    result := longRunningCalculation(ctx, 3)
    log.Printf("entering select (jobWithTimeout)")
    select {
    case <-ctx.Done():
        log.Println(ctx.Err())
        return
    case res := <-result:
        log.Println(res)
    }
}

func main() {
    jobWithTimeout()
}