Monday, 30 June 2025

Goroutine and Race condition, Deadlock

Content

- Goroutine
- sync.WaitGroup
- sync.Mutex => to fix
+ Race Condition (using: mu.Lock(), mu.Unlock())
+ Deadlock (using: mu.Lock(), mu.Unlock())

A goroutine is a lightweight thread managed by the Go runtime. It allows you to run functions concurrently—at the same time—as other code.


What is a goroutine?

In Go, when you use the keyword go before a function call, it runs that function in a new goroutine.

Think of it as telling Go:

“Hey, start this function in the background. I’ll keep doing other stuff while it runs.”


Keyword

  • go — this is the keyword to create a goroutine.


Basic Syntax

go functionName()

Or with an anonymous function:

go func() { // do something }()

Simple Example

package main import ( "fmt" "time" ) func sayHello() { fmt.Println("Hello from goroutine!") } func main() { go sayHello() // runs in a separate goroutine fmt.Println("Hello from main!") // Give the goroutine time to run time.Sleep(1 * time.Second) }

What Happens Here:

  1. main() starts.

  2. go sayHello() starts the sayHello() function concurrently.

  3. main() keeps running and prints its own message.

  4. time.Sleep() keeps the program running long enough to see the output from the goroutine.

sync.WaitGroup?

Think of sync.WaitGroup like a counter that helps your program wait until all background tasks (goroutines) are done.

package main import ( "fmt" "sync" "time" ) func doTask(id int, wg *sync.WaitGroup) { defer wg.Done() // When this function ends, signal "done" fmt.Println("Task", id, "starting...") time.Sleep(1 * time.Second) fmt.Println("Task", id, "done!") } func main() { var wg sync.WaitGroup wg.Add(2) // I'm going to wait for 2 tasks go doTask(1, &wg) go doTask(2, &wg) wg.Wait() // Wait for both tasks to finish fmt.Println("All tasks completed!") }

Output

Task 1 starting... Task 2 starting... Task 1 done! Task 2 done! All tasks completed!

Summary

KeywordMeaning
sync.WaitGroupTool to wait for goroutines
wg.Add(n)Say “I’m waiting for n tasks”
wg.Done()Say “I’m finished with my task”
wg.Wait()Pause until all tasks are done

Race Condition

Simple Definition:

A race condition happens when two or more goroutines access the same variable at the same time, and at least one of them writes to it.

Think of:

Two kids writing on the same whiteboard at the same time — the result is a mess.


Simple Example (with Race Condition):

package main import ( "fmt" "time" ) var count = 0 func add() { for i := 0; i < 1000; i++ { count++ // 🚨 not safe! } } func main() { go add() go add() time.Sleep(1 * time.Second) fmt.Println("Final count:", count) }

Problem:

You expect count = 2000, but it might be 1792, 1900, 2000, etc.

Because:

  • Both goroutines read and write at the same time.

  • They "race" to access count.


Fix with sync.Mutex (Mutual Exclusion Lock)

package main import ( "fmt" "sync" "time" ) var count = 0 var mu sync.Mutex func add() { for i := 0; i < 1000; i++ { mu.Lock() count++ mu.Unlock() } } func main() { go add() go add() time.Sleep(1 * time.Second) fmt.Println("Final count:", count) // Always 2000 now }

2. Deadlock

Simple Definition:

A deadlock happens when two or more goroutines are waiting for each other, and none can move forward.

Think of:

Two people trying to pass each other in a narrow hallway but both freeze, waiting forever.


Simple Deadlock Example:

package main import ( "sync" ) func main() { var mu sync.Mutex mu.Lock() mu.Lock() // 🚨 This will never happen — DEADLOCK! }

Problem:

  • mu.Lock() is called twice without mu.Unlock().

  • The second .Lock() waits forever → program hangs.


Deadlock with Channels

package main func main() { ch := make(chan int) ch <- 10 // 🚨 DEADLOCK! No one is reading from the channel }

❗ Why?

  • You’re sending a value to the channel.

  • But no goroutine is receiving from it → it waits forever.

Thank you.

No comments:

Post a Comment

Golang Advanced Interview Q&A