Golang - Concurrency - Goroutines and Channels Fundamentals

Publish date: 2025-09-27
Tags: <a href="https://programmercave.com/tags/Go/">Go</a>

Go was designed from the ground up for concurrency, and its approach is a major reason for its popularity. The model is built on two simple but powerful concepts: goroutines and channels.

The core philosophy is: “Don’t communicate by sharing memory; instead, share memory by communicating.”


1. Goroutines: Lightweight Threads

A goroutine is an independently executing function that runs concurrently with other code. You can think of it as a very lightweight thread, but it’s managed by the Go runtime, not the OS.

Analogy: Imagine you are a chef in a kitchen.

Key Features of Goroutines:

How to Start a Goroutine

It’s incredibly simple: just use the go keyword before a function call.

package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 3; i++ {
		fmt.Println(s)
		time.Sleep(100 * time.Millisecond)
	}
}

func main() {
	// Start a new goroutine that executes the say() function.
	go say("world")

	// The main function continues to execute in its own goroutine.
	say("hello")
}

Output (order may vary):

hello
world
hello
world
hello
world

The program doesn’t wait for the go say("world") goroutine to finish. The main function’s goroutine and the new goroutine run concurrently.


2. Channels: The Communication Pipes

Channels are the primary way for goroutines to communicate and synchronize with each other. A channel is a typed conduit, like a pipe, that you can send values into and receive values from.

Analogy: A channel is like a conveyor belt between two kitchen helpers.

Key Features of Channels:

How to Use Channels

package main

import "fmt"

// This function calculates the sum and sends the result to a channel.
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // Send sum to channel c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	// Create a channel to communicate the result.
	c := make(chan int)

	// Start two goroutines to sum the two halves of the slice.
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)

	// Receive the results from the channel.
	// This will block until both goroutines have sent their results.
	x, y := <-c, <-c

	fmt.Println(x, y, x+y) // -1 12 11
}

In this example, the main goroutine starts two sum goroutines. It then waits to receive two values from the channel c. It doesn’t matter which goroutine finishes first; main will block until both results are available, demonstrating the power of channels for both communication and synchronization.

Tags: <a href="https://programmercave.com/tags/Go/">Go</a>