The go language coroutine synchronization can be done through sync.waitgroup or chan. Chan’s method of synchronization only works in simple cases. When there are many coroutines and more independent coroutines are created from a single coroutine, chan’s approach can be very complicated when the coroutine needs to be notified of its end. To this end, go’s built-in Context package is encapsulated to provide a multi-coroutine safe cancellation or notification mechanism.

package main import ( "context" "fmt" "time" ) var key string = "name" func watch(ctx context.Context) { for { select { Case < -ctx.done (): fmt.println (ctx.value (key), "coroutine will stop ") return default: fmt.Println(ctx.Value(key), Sleep(2* time.second)}}} func main() {CTX,cancel := context.withcancel (context.background ()) valuectx1 Valuectx2 := context.WithValue(CTX, key, "[coroutine 1] ") go watch(valuectx1) valuectx2 := context.WithValue(CTX, key," Valuectx3 := context.WithValue(CTX, key) "[coroutine 3] ") go watch(valuectx3) time.sleep (10* time.second) fmt.println (" cancel() time.sleep (2* time.second)}Copy the code

Context. WithTimeout is used to set a timeout, which can be used to cancel the coroutine of the processing logic:

package main

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

func main() {
	ctx,cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	go handle(ctx, 1500*time.Millisecond)
	select {
	case <-ctx.Done():
		fmt.Println("main", ctx.Err())
	}
}

func handle(ctx context.Context, duration time.Duration) {
	select {
	case <-ctx.Done():
		fmt.Println("handle", ctx.Err())
		case <-time.After(duration):
			fmt.Println("process request with", duration)
	}

}
Copy the code

Note that the code above might print:

didi@bogon awesomeProject1 % go run test7.go 
main context deadline exceeded
handle context deadline exceeded
didi@bogon awesomeProject1 % go run test7.go 
main context deadline exceeded
didi@bogon awesomeProject1 % go run test7.go 
handle context deadline exceeded
main context deadline exceeded
Copy the code

Because multiple threads are running in parallel, the final standard output order cannot be guaranteed unless a synchronization lock is added.

Context.withdeadline is used to set a deadline. When the deadline is reached, the coroutine that needs to be synchronized can exit by reading the ctx.done () channel.

package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name, "is stopping now.") return default: fmt.Println(name, "is working now.") time.Sleep(1*time.Second) } } } func main() { ctx,cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) go worker(ctx, "Coroutine 1") time.sleep (6* time.second) cancel() time.sleep (2* time.second)}Copy the code

Output:

didi@bogon awesomeProject1 % go run test9.go coroutine 1 is working now. Coroutine 1 is working now. Coroutine 1 is working now. Coroutine 1 is working now. Coroutine 1 is working now. Stopping Now is stopping now.Copy the code

This demonstrates a timeout notification coroutine shutdown. If time.sleep (6time.second) is changed to time.sleep (3time.second) in the main coroutine, then the output is:

didi@bogon awesomeProject1 % go run test9.go coroutine 1 is working now. Coroutine 1 is working now. Coroutine 1 is working now. Stopping Now is stopping now.Copy the code

This is a case where one coroutine actively notifies the other that it is closed before the time has expired.