# Channels

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Concurrency
Source: https://www.devtutorials.ai/learn/go/concurrency/channels

> Go channels: create, send and receive, buffered channels and blocking, close, range and select, timeouts, channel directions, worker pools and done signals.

## What Are Channels?

**Channel** = a pipe that goroutines use to send values to each other.

- One goroutine **sends** a value into the channel.
- Another goroutine **receives** it from the other end.
- The channel makes them wait for each other at the right moment.

Go's motto: **"Do not communicate by sharing memory. Share memory by communicating."**

**Simple lesson:** a channel is a safe pipe for passing values between goroutines.

### A Real-World Example

Think of the answer-sheet tray between the exam hall and the marking room.

- Invigilators **drop** answer sheets into the tray.
- Teachers **pick** sheets out of the tray to mark.
- If the tray is empty, a teacher waits.
- If the tray is full, an invigilator waits.
- At the end, the office puts up a sign: "No more sheets". That is **closing** the channel.

Now the mapping:

- **The tray** = the channel.
- **Dropping a sheet** = `ch <- sheet` (send).
- **Picking a sheet** = `sheet := <-ch` (receive).
- **The tray's size** = the channel's buffer capacity.

You met goroutines in **Goroutines**.

There, each goroutine wrote to its own slot and `main` used a WaitGroup. Channels let goroutines **hand values** to each other instead.

## What You Will Learn

In this lesson, you will learn:

1. How to create channels, and send and receive values.
2. Buffered channels: capacity and blocking.
3. How to close a channel and loop over it with `range`.
4. How `select` waits on many channels at once.
5. Patterns for goroutine communication and data sharing.

## Channel Fundamentals

### Creating Channels

```go
ch := make(chan string)       // unbuffered channel of strings
nums := make(chan int, 3)     // buffered: holds up to 3 ints
```

- `chan T` = a channel that carries values of type `T`.
- Channels are made with `make`. The zero value of a channel is `nil`.

### Sending and Receiving Data

| Action | Code |
|---|---|
| Send | `ch <- value` |
| Receive into a variable | `v := <-ch` |
| Receive and throw away | `<-ch` |

- The arrow shows which way the value moves.

```go runnable title="Send from a goroutine, receive in main"
package main

import "fmt"

func main() {
	results := make(chan string)
	go func() {
		results <- "Asha scored 91"
	}()
	msg := <-results
	fmt.Println(msg)
}
```

```text
Asha scored 91
```

- `main` waits at `<-results` until the goroutine sends.
- No WaitGroup is needed: receiving the value **is** the waiting.

### Unbuffered Channels Hand Over Directly

**Unbuffered channel** = a channel with no storage. A send waits until a receiver takes the value.

Try sending and receiving. Switch between unbuffered and buffered.

Watch for **BLOCKED**:

- **Unbuffered:** a send waits for a receiver, and a receive waits for a sender.
- **Buffered:** a send waits only when the buffer is full.

**Simple lesson:** `ch <- v` sends, `<-ch` receives, and both wait until the other side is ready.

## Buffered Channels

### Capacity

**Buffered channel** = a channel with room for some values: `make(chan T, n)`.

```go runnable title="A buffer of 3"
package main

import "fmt"

func main() {
	tray := make(chan string, 3)
	tray <- "sheet 1"
	tray <- "sheet 2"
	fmt.Println(len(tray), cap(tray))
	fmt.Println(<-tray)
	fmt.Println(<-tray)
	fmt.Println(len(tray))
}
```

```text
2 3
sheet 1
sheet 2
0
```

- `cap(ch)` = how many values fit.
- `len(ch)` = how many are waiting right now.
- Values come out in the order they went in: **first in, first out**.

### Blocking Behavior

**Block** = wait at that line until something happens.

| Channel | Send blocks when | Receive blocks when |
|---|---|---|
| Unbuffered | no receiver is ready | no sender is ready |
| Buffered | the buffer is full | the buffer is empty |

### Deadlock

**Deadlock** = every goroutine is waiting, and none can ever continue.

```go
func main() {
	ch := make(chan int)
	ch <- 1          // waits for a receiver... that never comes
	fmt.Println(<-ch)
}
// fatal error: all goroutines are asleep - deadlock!
```

- The send waits forever, because the receive is on the next line of the **same** goroutine.
- The real `go` tool stops with the message above.
- In this lab, a deadlocked program is stopped after a few seconds instead.

**Simple lesson:** a buffered channel lets a sender run ahead until the buffer is full.

## Channel Operations

### close()

**close(ch)** = say "no more values will be sent".

- Only the **sender** should close a channel.
- Receiving from a closed, empty channel gives the zero value at once.
- Sending on a closed channel panics.

### Receiving with ok

```go runnable title="Is the channel closed?"
package main

import "fmt"

func main() {
	ch := make(chan int, 2)
	ch <- 91
	close(ch)
	v, ok := <-ch
	fmt.Println(v, ok)
	v, ok = <-ch
	fmt.Println(v, ok)
}
```

```text
91 true
0 false
```

- `ok` is `false` when the channel is closed **and** empty.

### range

`for v := range ch` receives until the channel is closed and empty. See **Loops**.

Press **Play** to watch a producer and a consumer.

- Without `close`, the `range` would wait forever after the last sheet: a deadlock.

### select

**select** = wait on several channel operations, and run whichever is ready first.

```go runnable title="Whichever answers first"
package main

import (
	"fmt"
	"time"
)

func main() {
	library := make(chan string)
	canteen := make(chan string)
	go func() {
		time.Sleep(30 * time.Millisecond)
		library <- "book is ready"
	}()
	go func() {
		time.Sleep(10 * time.Millisecond)
		canteen <- "lunch is ready"
	}()
	for i := 0; i < 2; i++ {
		select {
		case m := <-library:
			fmt.Println("library:", m)
		case m := <-canteen:
			fmt.Println("canteen:", m)
		}
	}
}
```

```text
canteen: lunch is ready
library: book is ready
```

- Each `select` waits until **one** case can go ahead, then runs it.
- The canteen answers first, so its case runs first.

### Timeouts with select

```go runnable title="Do not wait forever"
package main

import (
	"fmt"
	"time"
)

func main() {
	reply := make(chan string)
	go func() {
		time.Sleep(200 * time.Millisecond)
		reply <- "too late"
	}()
	select {
	case r := <-reply:
		fmt.Println(r)
	case <-time.After(50 * time.Millisecond):
		fmt.Println("timed out after 50ms")
	}
}
```

```text
timed out after 50ms
```

- `time.After(d)` gives a channel that receives a value after `d`.
- A `default:` case runs at once if no other case is ready, so `select` never waits.

**Simple lesson:** `close` ends a stream, `range` reads it to the end, and `select` waits on many channels at once.

## Channel Communication Patterns

### Goroutine Communication

**Worker pool** = a few goroutines that take jobs from one channel and put results on another.

```go runnable title="Three workers mark ten papers"
package main

import (
	"fmt"
	"sort"
)

type result struct {
	roll  int
	marks int
}

func worker(jobs <-chan int, results chan<- result) {
	for roll := range jobs {
		results <- result{roll, 50 + roll*4}
	}
}

func main() {
	jobs := make(chan int, 10)
	results := make(chan result, 10)

	for w := 1; w <= 3; w++ {
		go worker(jobs, results)
	}
	for roll := 1; roll <= 10; roll++ {
		jobs <- roll
	}
	close(jobs)

	var all []result
	for i := 0; i < 10; i++ {
		all = append(all, <-results)
	}
	sort.Slice(all, func(i, j int) bool { return all[i].roll < all[j].roll })
	fmt.Println(all[0], all[9], len(all))
}
```

```text
{1 54} {10 90} 10
```

- **`<-chan int`** = a receive-only channel. **`chan<- result`** = a send-only channel.
- These direction types stop a worker from using a channel the wrong way.
- Results arrive in any order, so they are sorted before printing.

### Data Sharing

- **Pass ownership:** when a value is sent, the sender should stop using it. The receiver now owns it.
- **One writer:** let one goroutine own a piece of data, and let others send it requests through a channel.
- **Signal "done":** a `chan struct{}` with `close(done)` tells many goroutines to stop at once.

```go runnable title="A done channel"
package main

import "fmt"

func main() {
	done := make(chan struct{})
	finished := make(chan string)
	go func() {
		<-done
		finished <- "cleaner stopped"
	}()
	close(done)
	fmt.Println(<-finished)
}
```

```text
cleaner stopped
```

- `struct{}` carries no data. The signal is the close itself.
- Every goroutine waiting on `<-done` wakes up when it is closed.

Common channel traps:

- Sending on a closed channel panics.
- Closing a channel twice panics.
- Receiving from a `nil` channel waits forever.

**Easy way to remember**

- `ch <- v` = put in. `<-ch` = take out.
- Unbuffered = hand to hand. Buffered = a tray of size n.
- The sender closes. The receiver ranges.

**Simple lesson:** channels connect goroutines: jobs go in, results come out, and closing a channel signals the end.

## TL;DR

1. **Channel** = `make(chan T)` or `make(chan T, n)`. A pipe between goroutines.
2. **Send** = `ch <- v`. **Receive** = `v := <-ch`.
3. **Unbuffered** = sender and receiver meet. **Buffered** = room for `n` values.
   - **Blocking** = waiting until the other side is ready.
   - **Deadlock** = everyone waits forever.
4. **close(ch)** = no more values. `v, ok := <-ch` → `ok` is false when closed and empty.
5. **range** = `for v := range ch` until closed.
6. **select** = wait on many channels. `time.After` for timeouts.
7. **Directions** = `chan<- T` send-only, `<-chan T` receive-only.
8. **Patterns** = worker pool, pass ownership, done channel.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Send and receive in the same goroutine on an unbuffered channel | It hands itself the value | Another goroutine must receive | Deadlock. |
| Forgetting `close` before `range` | `range` stops by itself | It waits until the channel is closed | Deadlock at the end. |
| The receiver closes the channel | Anyone can close | Only the sender closes | `send on closed channel` panic. |
| Closing twice | It is harmless | It panics | `close of closed channel` |
| `select` with no timeout on a slow channel | It always answers | It may wait forever | A stuck program. Add `time.After`. |
| Expecting worker results in order | They come in send order | Any order | Sort or index the results. |

## Check Yourself

## Exercises

## What to Remember

- A channel passes values between goroutines: `ch <- v`, `v := <-ch`.
- Unbuffered channels hand over directly. Buffered ones hold up to `n` values.
- The sender closes. `range` reads until closed.
- `select` waits on many channels, and `time.After` adds a timeout.
- Worker pools: jobs in, results out, and sort results if order matters.

Next: **Synchronization**. You will learn how to protect shared data with WaitGroups, mutexes and atomic operations.
