# Goroutines

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

> Concurrency in Go: concurrency vs parallelism, starting goroutines with go, waiting with WaitGroup, anonymous goroutines, what happens when main ends, and concurrent processing.

## What Is Concurrency?

**Concurrency** = a program working on many jobs during the same period of time, switching between them.

**Goroutine** = a very light worker that runs a function at the same time as the rest of the program.

Go was built for this. Starting a goroutine takes one word: `go`.

**Simple lesson:** concurrency lets one program handle many jobs at once, and goroutines are how Go does it.

### A Real-World Example

Think of the school canteen at lunch break.

- **One cook, one job at a time:** make tea, then wait for it, then make sandwiches, then fry samosas. The line grows long.
- **One cook who switches:** while the tea boils, make sandwiches. While the samosas fry, serve tea. Much faster.
- **Three cooks:** each makes one item, truly at the same moment.

Now the mapping:

- **Switching between jobs** = concurrency.
- **Three cooks at the same moment** = parallelism.
- **Each job a cook works on** = a goroutine.

Goroutines are much lighter than operating-system threads.

- A new goroutine starts with only a few kilobytes of memory.
- A program can run thousands, even millions, of them.
- Go's **scheduler** decides which goroutine runs on which CPU core.

## What You Will Learn

In this lesson, you will learn:

1. What concurrency is, and how it differs from parallelism.
2. How to start goroutines with the `go` keyword.
3. How to wait for goroutines to finish.
4. How to start goroutines from anonymous functions.
5. What happens to goroutines when `main` ends.
6. A common use: processing many items at once.

## Concurrency Fundamentals

### Concurrency vs Parallelism

- Go programs are written **concurrently**.
- On a computer with many cores, Go also runs them **in parallel**.
- In this browser lab, goroutines take turns on one thread. They are concurrent, but not parallel.

**Simple lesson:** concurrency is many jobs in progress, parallelism is many jobs at the same instant.

## Goroutines

### The go Keyword

Put `go` in front of a function call, and that call runs in a new goroutine.

```go
go sendReminder("Asha")   // runs at the same time
fmt.Println("main goes on")
```

- `main` does **not** wait for `sendReminder` to finish.
- `main` itself runs in a goroutine too: the **main goroutine**.

### Running Concurrent Tasks

Pick a mode and press **Run**.

Compare the three modes:

- **No go keyword:** the total time is the sum of all jobs.
- **go + WaitGroup:** the total time is the slowest job.
- **go, but no Wait:** the program ends before the work is done.

### Creating Goroutines and Waiting for Them

**sync.WaitGroup** = a counter that lets `main` wait until a group of goroutines is done. You will learn it fully in **Synchronization**.

- `wg.Add(1)` before starting each goroutine.
- `defer wg.Done()` inside the goroutine.
- `wg.Wait()` blocks until the counter is back to zero.

```go runnable title="Three goroutines, then wait"
package main

import (
	"fmt"
	"sync"
	"time"
)

func markPapers(class string, minutes int, wg *sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(time.Duration(minutes) * 10 * time.Millisecond)
	fmt.Println(class, "done")
}

func main() {
	var wg sync.WaitGroup

	wg.Add(3)
	go markPapers("8A", 3, &wg)
	go markPapers("8B", 1, &wg)
	go markPapers("8C", 2, &wg)

	wg.Wait()
	fmt.Println("all done")
}
```

```text
8B done
8C done
8A done
all done
```

- The shortest job finishes first, not the first one started.
- One after another, the jobs would take 30 + 10 + 20 = 60 ms. Together, they take about 30 ms: the slowest one.
- Pass the WaitGroup as a **pointer**: `&wg`. Copying it would break it.

### Goroutine Execution

- The order goroutines run in is **not fixed**. Never depend on it.
- Above, the sleeps made the order clear. Without them, any order is possible.
- If you need a fixed order, collect results and sort them, or use channels. See **Channels**.

**Simple lesson:** `go f()` starts f at the same time, and a WaitGroup lets `main` wait for it.

## Anonymous Goroutines

### Goroutines with Anonymous Functions

Often the goroutine's work is short, so it is written as a function literal. See **Functions**.

- `go func(n int) { ... }(i)` starts a goroutine running a function literal.
- Passing `i` as an argument gives each goroutine its own copy.
- Since Go 1.22, each loop trip also gets its own `i`, so using `i` directly is safe too.
- Each goroutine writes a **different** slot. Writing the **same** variable needs a lock. See **Synchronization**.

```go runnable title="Results in a fixed order"
package main

import (
	"fmt"
	"strings"
	"sync"
)

func main() {
	names := []string{"asha", "ravi", "meena"}
	badges := make([]string, len(names))
	var wg sync.WaitGroup
	for i, name := range names {
		wg.Add(1)
		go func() {
			defer wg.Done()
			badges[i] = strings.ToUpper(name) + " (8A)"
		}()
	}
	wg.Wait()
	fmt.Println(badges)
}
```

```text
[ASHA (8A) RAVI (8A) MEENA (8A)]
```

**Simple lesson:** start short jobs with `go func() { ... }()`, and give each goroutine its own place to write.

## Goroutine Behavior

### Concurrent Execution

```go runnable varies title="The order can change"
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	for _, name := range []string{"Asha", "Ravi", "Meena"} {
		wg.Add(1)
		go func() {
			defer wg.Done()
			fmt.Println("hello from", name)
		}()
	}
	wg.Wait()
}
```

- On a real computer, the three lines may come out in any order.
- In this lab, goroutines take turns on one thread, so the order often looks the same. Do not rely on it.

### Program Termination and Goroutines

When `main` returns, the **whole program ends**, even if other goroutines are still working.

```go runnable varies title="main does not wait by itself"
package main

import (
	"fmt"
	"time"
)

func main() {
	go func() {
		time.Sleep(50 * time.Millisecond)
		fmt.Println("this line is usually never printed")
	}()
	fmt.Println("main is done")
}
```

- `main` prints and returns at once.
- The goroutine is stopped before its `Println`.
- Always wait for goroutines you care about: `WaitGroup` or channels.

Never use `time.Sleep` in `main` to "wait long enough" for goroutines.

It is either too long (slow) or too short (lost work). Use `sync.WaitGroup` or channels.

**Easy way to remember**

- `go f()` = start f, do not wait.
- `wg.Add`, `wg.Done`, `wg.Wait` = wait for a group.
- When `main` ends, every goroutine ends.

**Simple lesson:** goroutines run in no fixed order, and they all stop when `main` returns.

## Common Use Cases

### Concurrent Processing

**Concurrent processing** = splitting a big job into pieces that run at the same time.

Good fits:

- checking many websites or files at once
- resizing many photos
- handling many users' requests on a server
- sending many notifications

```go runnable title="Check every class's attendance at once"
package main

import (
	"fmt"
	"sync"
)

func countPresent(marks []bool) int {
	n := 0
	for _, present := range marks {
		if present {
			n++
		}
	}
	return n
}

func main() {
	classes := map[string][]bool{
		"8A": {true, true, false, true},
		"8B": {true, false, false},
		"8C": {true, true, true},
	}
	order := []string{"8A", "8B", "8C"}
	present := make([]int, len(order))

	var wg sync.WaitGroup
	for i, name := range order {
		wg.Add(1)
		go func() {
			defer wg.Done()
			present[i] = countPresent(classes[name])
		}()
	}
	wg.Wait()

	for i, name := range order {
		fmt.Println(name, "present:", present[i])
	}
}
```

```text
8A present: 3
8B present: 1
8C present: 3
```

- One goroutine per class.
- Each writes its own slot in `present`, so there is no clash.
- Printing happens after `wg.Wait()`, in a fixed order.

**Simple lesson:** split independent work into goroutines, give each its own result slot, and wait for them all.

## TL;DR

1. **Concurrency** = many jobs in progress. **Parallelism** = many jobs at the same instant.
2. **Goroutine** = a light worker. Start one with `go f()`.
3. **main** does not wait. When it returns, all goroutines stop.
4. **Wait** with `sync.WaitGroup`: `Add(n)`, `defer Done()`, `Wait()`.
   - Pass it as `&wg`.
5. **Anonymous goroutine** = `go func() { ... }()`.
6. **Order** is not fixed. Write results to separate slots, or use channels.
7. **Use** = concurrent processing of independent jobs.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| No `wg.Wait()` | `main` waits for goroutines | It does not | Work is lost when `main` ends. |
| `time.Sleep` to wait | A sleep is enough | Use a WaitGroup | Flaky, slow programs. |
| Passing `wg` by value | It is fine | Pass `&wg` | Wait never returns, or Done panics. |
| `wg.Add(1)` inside the goroutine | It counts in time | Call `Add` before `go` | `Wait` may return too early. |
| Expecting output in start order | Goroutines run in order | Order is not fixed | Confusing, changing output. |
| Many goroutines writing one variable | It is safe | It is a data race | Wrong totals. See **Synchronization**. |

## Check Yourself

## Exercises

## What to Remember

- Concurrency is many jobs in progress. Parallelism is many jobs at the same instant.
- `go f()` starts a goroutine. It does not wait.
- Use `sync.WaitGroup` to wait: `Add`, `Done`, `Wait`.
- Goroutines run in no fixed order. Give each its own result slot.
- When `main` returns, every goroutine stops.

Next: **Channels**. You will learn how goroutines send values to each other safely.
