What the Context

import "context"

Purpose?

Go servers treat each incoming request in its own goroutine


Each request handler may spawn other goroutines to access hardware( IO operation)

 

When a request is cancelled or timeout, goroutines should exit quickly so the system can reclaim resources

 

type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

Core of Context

Done() <-chan struct{}

The Done method returns a channel that acts as a cancelation signal to functions running on behalf of the Context: when the channel is closed, the functions should abandon their work and return

Err() error

The Err method returns an error indicating why the Context was canceled.

There are two ways to spawn Context

context.TODO()

context.Background()
func main() {

    ctx := context.TODO()
}

First, we create our context

func printNums() {

    for {
      
        time.Sleep(1 * time.Second)
	println(rand.Int())

    }

}

Next, we write a func that we will treat as a goroutine doing dumb work.
Like printing numbers

WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel function

Takes in parent context

Spits out new context, and a cancel function

package main

import (
	"context"
	"math/rand"
	"time"
)

func main() {

	todo := context.TODO()
	ctx, cancelFunc := context.WithCancel(todo)
	go printNums(ctx)

}

func printNums(ctx context.Context) {

    for {
      
        time.Sleep(1 * time.Second)
	println(rand.Int())

    }

}
package main

import (
	"context"
	"math/rand"
	"time"
)

func main() {

	todo := context.TODO()
	ctx, cancelFunc := context.WithCancel(todo)
	go printNums(ctx)

	//wait and see numbers getting print
	time.Sleep(5 * time.Second)

	//cancel the context
	cancelFunc()

	//wait and see if its still printing numbers
	time.Sleep(5 * time.Second)
}
func printNums(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(1 * time.Second)
			println(rand.Int())

		}

	}
}
context.WithTimeout(ctx context.Context, d time.Duration)

WithTimeout

func main() {

	todo := context.TODO()
	ctx, cancelFunc := context.WithTimeout(todo, 5*time.Second)
	defer cancelFunc()
	go printNums(ctx)
	//wait and see if its still printing numbers
	time.Sleep(10 * time.Second)
}

func printNums(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(1 * time.Second)
			println(rand.Int())

		}

	}
}
context.WithTimeout(ctx context.Context, d time.Duration)

WithDeadline

func main() {

	todo := context.TODO()
	d := time.Now().Add(5 * time.Second)
	ctx, cancelFunc := context.WithDeadline(todo, d)
	defer cancelFunc()
	go printNums(ctx)
	//wait and see if its still printing numbers
	time.Sleep(10 * time.Second)
}

func printNums(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(1 * time.Second)
			println(rand.Int())

		}

	}
}

Context Tree

when a parent context is cancelled, all their child's contexts are cancelled as well.

 

 

func main() {
	todo := context.TODO()
	d := time.Now().Add(5 * time.Second)
	ctx, cancelFunc := context.WithDeadline(todo, d)
	defer cancelFunc()
	go printNums(ctx)
	<-ctx.Done()
	println("5 seconds complete")
	//wait and see if its still printing numbers
	time.Sleep(5 * time.Second)

}

func printMoreNums(ctx context.Context) {
	time.Sleep(10 * time.Millisecond)
	println(rand.Int())
}
func printNums(ctx context.Context) {
	go printMoreNums(ctx)
	go printMoreNums(ctx)
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(1 * time.Second)
			println(rand.Int())

		}
	}
}

Fin

Made with Slides.com