Lesson 33 – Channels | Dataplexa

Channels in Go

Channels are Go’s built-in mechanism for communication between goroutines. They allow goroutines to safely send and receive data without explicit locks. In this lesson, you will learn how channels work, why they exist, and how to use them correctly.


Why Channels Exist

Goroutines run concurrently, but concurrency introduces a major challenge: safe data sharing.

Instead of sharing memory directly, Go encourages a different approach:

“Do not communicate by sharing memory; share memory by communicating.”

Channels implement this philosophy by allowing goroutines to exchange values safely.


What Is a Channel?

A channel is a typed conduit through which you can send and receive values.

Each channel has:

  • A specific data type
  • A sender
  • A receiver

Creating a Channel

Channels are created using the make function.

ch := make(chan int)

This creates an unbuffered channel that can send and receive integers.


Sending and Receiving Data

Use the <- operator to send and receive values.

ch <- 10        // send
value := <-ch  // receive

Send and receive operations block until the other side is ready.


Basic Channel Example

package main

import "fmt"

func main() {
    ch := make(chan string)

    go func() {
        ch <- "Hello from goroutine"
    }()

    message := <-ch
    fmt.Println(message)
}

Here, the main goroutine waits until it receives data from the channel.


Blocking Behavior Explained

Channels synchronize goroutines automatically:

  • Sending blocks until a receiver is ready
  • Receiving blocks until a sender is ready

This eliminates many race conditions by design.


Real-World Example: Data Processing

Consider processing sensor readings from multiple devices.

func sensor(id int, ch chan int) {
    ch <- id * 10
}

func main() {
    ch := make(chan int)

    for i := 1; i <= 3; i++ {
        go sensor(i, ch)
    }

    for i := 1; i <= 3; i++ {
        fmt.Println("Reading:", <-ch)
    }
}

Each goroutine sends data back to the main routine safely.


Channel Direction (Send-Only & Receive-Only)

Channels can be restricted to one direction for better safety.

func sendData(ch chan<- int) {
    ch <- 100
}

func receiveData(ch <-chan int) {
    fmt.Println(<-ch)
}

This prevents accidental misuse of channels.


Closing a Channel

Channels can be closed to signal that no more data will be sent.

close(ch)

Closing is usually done by the sender, not the receiver.


Receiving from a Closed Channel

You can detect whether a channel is closed using the “comma ok” pattern.

value, ok := <-ch
if !ok {
    fmt.Println("Channel closed")
}

Common Channel Mistakes

  • Sending on a closed channel (panic)
  • Forgetting to receive (deadlock)
  • Closing a channel multiple times
  • Using channels without goroutines

When to Use Channels

  • Goroutine communication
  • Work distribution
  • Pipeline processing
  • Event signaling

Practice Exercises

Exercise 1

Create a program where one goroutine sends five numbers into a channel and the main goroutine prints them.

Exercise 2

Modify the program to close the channel and detect when it is closed.


Key Takeaways

  • Channels enable safe communication between goroutines
  • They block by default, ensuring synchronization
  • Closing channels signals completion
  • Directional channels improve safety

What’s Next?

In the next lesson, you will learn about Buffered Channels and how they improve performance by allowing asynchronous communication.