DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • 2-Tier Architecture vs 3-Tier Architecture in DBMS
  • Fluent-API: Creating Easier, More Intuitive Code With a Fluent API
  • Jakarta NoSQL 1.0: A Way To Bring Java and NoSQL Together
  • Container Checkpointing in Kubernetes With a Custom API

Trending

  • DGS GraphQL and Spring Boot
  • Building Resilient Identity Systems: Lessons from Securing Billions of Authentication Requests
  • The Role of AI in Identity and Access Management for Organizations
  • Monolith: The Good, The Bad and The Ugly
  1. DZone
  2. Coding
  3. Languages
  4. Contexts in Go: A Comprehensive Guide

Contexts in Go: A Comprehensive Guide

Learn how to effectively use contexts in Go for managing execution time, task cancellation, passing data between goroutines, and more.

By 
Suleiman Dibirov user avatar
Suleiman Dibirov
DZone Core CORE ·
Jul. 05, 24 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
6.9K Views

Join the DZone community and get the full member experience.

Join For Free

Contexts in Go provide a standard way to pass metadata and control signals between goroutines. They are mainly used to manage task execution time, data passing, and operation cancellation. This article covers different types of contexts in Go and examples of how to use them.

Introduction to Contexts

Contexts in Go are represented by the context.Context interface, which includes methods for getting deadlines, cancellation, values, and done channels. The primary package for working with contexts is context.

Go
 
package context

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}


Context Types

There are six main functions to create contexts:

  1. context.Background(): Returns an empty context; It is usually used as the root context for the entire application.
  2. context.TODO(): Returns a context that can be used when a context is required but not yet defined; It signals that the context needs further work.
  3. context.WithCancel(parent Context): Returns a derived context that can be canceled by calling the cancel function
  4. context.WithDeadline(parent Context, d time.Time): Returns a derived context that automatically cancels at a specified time (deadline)
  5. context.WithTimeout(parent Context, timeout time.Duration): Similar to the WithDeadline, but the deadline is set by a duration
  6. context.WithValue(parent Context, key, val interface{}): Returns a derived context that contains a key-value pair

Examples of Using Contexts

Context With Cancelation

A context with cancelation is useful when you need to stop a goroutine based on an event.

Go
 
package main

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

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        select {
        case <-time.After(2 * time.Second):
            fmt.Println("Operation completed")
        case <-ctx.Done():
            fmt.Println("Operation canceled")
        }
    }()
    
    // try to change this value to 3 and execute again
    time.Sleep(1 * time.Second)
    cancel()
    time.Sleep(2 * time.Second)
}


Context With Timeout

This context automatically cancels after a specified duration.

Go
 
package main

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

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    
    go func() {
        select {
        case <-time.After(3 * time.Second):
            fmt.Println("Operation completed")
        case <-ctx.Done():
            fmt.Println("Operation timed out")
        }
    }()
    
    // try to change this value to 2 and execute again
    time.Sleep(4 * time.Second)
}


Context With Deadline

A context with a deadline is similar to a context with a timeout, but the time is set as a specific value.

Go
 
package main

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

func main() {
  	// try to change this value to 3 and execute again
    deadline := time.Now().Add(2 * time.Second)
    ctx, cancel := context.WithDeadline(context.Background(), deadline)
    defer cancel()
    
    go func() {
        select {
        case <-time.After(3 * time.Second):
            fmt.Println("Operation completed")
        case <-ctx.Done():
            fmt.Println("Operation reached deadline")
        }
    }()
    
    time.Sleep(4 * time.Second)
}


Context With Values

Contexts can store arbitrary data as key-value pairs. This is useful for passing parameters and settings to handlers.

Go
 
package main

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

func main() {
	ctx := context.WithValue(context.Background(), "key", "value")

	go func(ctx context.Context) {
		if v := ctx.Value("key"); v != nil {
			fmt.Println("Value found:", v)
		} else {
			fmt.Println("No value found")
		}
	}(ctx)

	time.Sleep(1 * time.Second)
}


Applying Contexts

Contexts are widely used in various parts of Go applications, including network servers, databases, and client requests. They help properly manage task execution time, cancel unnecessary operations, and pass data between goroutines.

Using in HTTP Servers

Go
 
package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
)

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    select {
    case <-time.After(5 * time.Second):
        fmt.Fprintf(w, "Request processed")
    case <-ctx.Done():
        fmt.Fprintf(w, "Request canceled")
    }
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}


This code sets up an HTTP server that handles requests with a context-aware handler. It either completes after 5 seconds or responds if the request is canceled.

Using in Databases

Go
 
package main

import (
    "context"
    "database/sql"
    "fmt"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

func queryDatabase(ctx context.Context, db *sql.DB) {
    query := "SELECT sleep(5)"
    rows, err := db.QueryContext(ctx, query)
    if err != nil {
        fmt.Println("Query error:", err)
        return
    }
    defer rows.Close()

    for rows.Next() {
        var result string
        if err := rows.Scan(&result); err != nil {
            fmt.Println("Scan error:", err)
            return
        }
        fmt.Println("Result:", result)
    }
}

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
    if err != nil {
        fmt.Println("Database connection error:", err)
        return
    }
    defer db.Close()

    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    queryDatabase(ctx, db)
}


Here, we connect to a MySQL database and execute a query with a context timeout of 3 seconds. If the query takes longer, it is canceled, and an error message is printed.

Using in Goroutines

Go
 
package main

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

func worker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d stopped\n", id)
            return
        case <-time.After(1 * time.Second):
            fmt.Printf("Worker %d working\n", id)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    for i := 1; i <= 3; i++ {
        go worker(ctx, i)
    }

    time.Sleep(3 * time.Second)
    cancel()
    time.Sleep(1 * time.Second)
}


In this example, the code spawns three worker goroutines that print status messages every second. The workers stop when the main function cancels the context after 3 seconds.

Using in an API Request With a Deadline

Go
 
package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
)

func fetchAPI(ctx context.Context, url string) {
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        fmt.Println("Request creation error:", err)
        return
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Request error:", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode == http.StatusOK {
        fmt.Println("API request succeeded")
    } else {
        fmt.Println("API request failed with status:", resp.StatusCode)
    }
}

func main() {
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
    defer cancel()

    fetchAPI(ctx, "http://example.com/api")
}


This example demonstrates making an API request with a 2-second deadline. If the request is not completed within this timeframe, it is canceled, ensuring that the program does not wait indefinitely.

Conclusion

Contexts in Go are a powerful tool for managing execution time, cancelation, and data passing between goroutines. Using contexts correctly helps avoid resource leaks, ensures timely task completion, and improves code structure and readability. Various types of contexts, such as those with cancellation, timeout, deadline, and values, provide flexible task management in Go applications.

API Database Go (programming language) Interface (computing) Timeout (computing)

Opinions expressed by DZone contributors are their own.

Related

  • 2-Tier Architecture vs 3-Tier Architecture in DBMS
  • Fluent-API: Creating Easier, More Intuitive Code With a Fluent API
  • Jakarta NoSQL 1.0: A Way To Bring Java and NoSQL Together
  • Container Checkpointing in Kubernetes With a Custom API

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!