The Cancel Pattern uses a signaling channel (commonly named done
) to:
- Notify goroutines when they should stop.
- Allow for safe and controlled termination of goroutines.
This pattern ensures that goroutines do not run indefinitely, preventing potential resource leaks or unwanted side effects.
package main
import (
"fmt"
"time"
)
func main() {
// Function that performs work until the 'done' channel is closed
doWork := func(done <-chan interface{}, strings <-chan string) <-chan interface{} {
terminated := make(chan interface{})
go func() {
defer fmt.Println("doWork exited.")
defer close(terminated)
for {
select {
case s := <-strings:
fmt.Println(s)
case <-done:
// Exit when 'done' is closed or receives a signal
return
}
}
}()
return terminated
}
done := make(chan interface{})
terminated := doWork(done, nil)
go func() {
// Stop the doWork goroutine after 1 second
time.Sleep(1 * time.Second)
fmt.Println("Canceling doWork goroutine...")
close(done)
}()
<-terminated
fmt.Println("Done.")
}
-
done
Channel:- Acts as a signal to stop the goroutine.
- Closing the channel (
close(done)
) tells the goroutine to exit.
-
strings
Channel:- Represents a stream of data (in this case, string values) for the goroutine to process.
-
terminated
Channel:- Used to signal the caller (main function) that the goroutine has exited.
-
The
doWork
function:- Starts a goroutine that listens to the
done
andstrings
channels using aselect
statement. - If the
done
channel is closed, the goroutine safely exits.
- Starts a goroutine that listens to the
-
The main function:
- Closes the
done
channel after 1 second, signaling the goroutine to terminate.
- Closes the
When running the code, the output will look like this:
Canceling doWork goroutine...
doWork exited.
Done.
-
Safe Goroutine Termination:
- Prevents uncontrolled goroutine execution, which can lead to memory leaks or excessive resource consumption.
-
Simple and Effective:
- Using a
done
channel is straightforward and works well for basic use cases.
- Using a
-
Decoupled Signal Handling:
- The goroutine does not depend on specific implementation details; it just listens for a stop signal.