Golang Synchronization Tools Quiz
35 comprehensive questions on Golang's synchronization tools, covering sync.Mutex, sync.RWMutex, sync.WaitGroup, sync.Once, and common race-condition mistakes — with 18 code examples demonstrating thread-safe patterns.
Question 1
What is a race condition in Go?
var counter int
func increment() {
counter++
}
func main() {
go increment()
go increment()
time.Sleep(time.Second)
fmt.Println(counter) // May not be 2
}Question 2
How does sync.Mutex work?
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}Question 3
What is the difference between sync.Mutex and sync.RWMutex?
var rwmu sync.RWMutex
func readData() {
rwmu.RLock()
// read operations
rwmu.RUnlock()
}
func writeData() {
rwmu.Lock()
// write operations
rwmu.Unlock()
}Question 4
How do you use sync.WaitGroup?
var wg sync.WaitGroup
func worker(id int) {
defer wg.Done()
fmt.Printf("Worker %d done\n", id)
}
func main() {
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(i)
}
wg.Wait()
fmt.Println("All workers done")
}Question 5
What does sync.Once guarantee?
var once sync.Once
var initialized bool
func initResource() {
once.Do(func() {
// initialization code
initialized = true
})
}Question 6
What is a deadlock in Go?
var mu1, mu2 sync.Mutex
func goroutine1() {
mu1.Lock()
time.Sleep(time.Second)
mu2.Lock() // deadlock if goroutine2 holds mu2 and waits for mu1
mu2.Unlock()
mu1.Unlock()
}Question 7
How do you fix a race condition with a counter?
var counter int
var mu sync.Mutex
func increment() {
mu.Lock()
counter++
mu.Unlock()
}Question 8
When should you use sync.RWMutex over sync.Mutex?
var data map[string]int
var rwmu sync.RWMutex
func read(key string) int {
rwmu.RLock()
defer rwmu.RUnlock()
return data[key]
}
func write(key string, value int) {
rwmu.Lock()
defer rwmu.Unlock()
data[key] = value
}Question 9
What is a common mistake with sync.WaitGroup?
var wg sync.WaitGroup
func main() {
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// work
}()
}
wg.Wait() // Waits forever if goroutines don't start
}Question 10
How do you implement a thread-safe singleton with sync.Once?
type singleton struct{}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}Question 11
What is a livelock?
Question 12
How do you avoid deadlock with multiple mutexes?
var mu1, mu2 sync.Mutex
func safe() {
mu1.Lock()
defer mu1.Unlock()
mu2.Lock()
defer mu2.Unlock()
// Always acquire in same order
}Question 13
What is the copy lock problem?
type Counter struct {
mu sync.Mutex
value int
}
func (c Counter) Increment() { // Wrong: receiver by value
c.mu.Lock() // Locks copy, not original
c.value++
c.mu.Unlock()
}Question 14
How do you implement a thread-safe map?
type SafeMap struct {
mu sync.RWMutex
data map[string]int
}
func (sm *SafeMap) Get(key string) int {
sm.mu.RLock()
defer sm.mu.RUnlock()
return sm.data[key]
}
func (sm *SafeMap) Set(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.data[key] = value
}Question 15
What is the double-checked locking problem?
var instance *singleton
var mu sync.Mutex
func GetInstance() *singleton {
if instance == nil { // Check without lock
mu.Lock()
if instance == nil {
instance = &singleton{}
}
mu.Unlock()
}
return instance
}Question 16
How do you coordinate multiple goroutines with WaitGroup?
func processBatch(items []int) {
var wg sync.WaitGroup
results := make(chan int, len(items))
for _, item := range items {
wg.Add(1)
go func(item int) {
defer wg.Done()
results <- process(item)
}(item)
}
wg.Wait()
close(results)
for result := range results {
fmt.Println(result)
}
}Question 17
What is a starvation issue with Mutex?
Question 18
How do you implement a read-write lock correctly?
var rwmu sync.RWMutex
var data int
func reader() {
rwmu.RLock()
// multiple readers can be here
_ = data
rwmu.RUnlock()
}
func writer() {
rwmu.Lock()
// exclusive access
data = 42
rwmu.Unlock()
}Question 19
What is the problem with this WaitGroup usage?
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
go func(i int) {
wg.Add(1) // Wrong: Add called in goroutine
defer wg.Done()
fmt.Println(i)
}(i)
}
wg.Wait()
}Question 20
How do you implement lazy initialization with sync.Once?
type Config struct {
data map[string]string
}
var config *Config
var once sync.Once
func GetConfig() *Config {
once.Do(func() {
config = loadConfig() // expensive operation
})
return config
}Question 21
What is a goroutine leak caused by synchronization?
func worker(ch chan int) {
for {
select {
case v := <-ch:
process(v)
case <-time.After(time.Second):
return // timeout
}
}
}
func main() {
ch := make(chan int)
go worker(ch)
// Forgot to close ch or send quit signal
time.Sleep(time.Minute)
}Question 22
How do you fix a race condition in a slice?
type SafeSlice struct {
mu sync.Mutex
data []int
}
func (ss *SafeSlice) Append(value int) {
ss.mu.Lock()
defer ss.mu.Unlock()
ss.data = append(ss.data, value)
}Question 23
What is the difference between Lock() and RLock()?
var rwmu sync.RWMutex
func read() {
rwmu.RLock() // Allows other RLock calls
defer rwmu.RUnlock()
}
func write() {
rwmu.Lock() // Blocks all other locks
defer rwmu.Unlock()
}Question 24
How do you implement a barrier with WaitGroup?
func barrierExample() {
var wg sync.WaitGroup
var mu sync.Mutex
var counter int
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// phase 1
mu.Lock()
counter++
mu.Unlock()
wg.Wait() // barrier: wait for all to finish phase 1
// phase 2
fmt.Printf("Goroutine %d in phase 2\n", id)
}(i)
}
wg.Wait() // wait for all to complete
}Question 25
What is a common race condition in map access?
var m map[string]int // concurrent map writes
func add(key string, value int) {
m[key] = value // panic: concurrent map writes
}Question 26
How do you use sync.Once for error handling?
var once sync.Once
var err error
func initResource() error {
once.Do(func() {
err = loadResource() // capture error
})
return err
}Question 27
What is the lock hierarchy anti-pattern?
func A() {
muA.Lock()
B() // B tries to lock muA while holding muB
muA.Unlock()
}
func B() {
muB.Lock()
A() // A tries to lock muB while holding muA
muB.Unlock()
}Question 28
How do you implement a semaphore with channels?
func semaphore(n int) chan struct{} {
return make(chan struct{}, n)
}
func worker(sem chan struct{}, id int) {
sem <- struct{}{} // acquire
defer func() { <-sem }() // release
work(id)
}Question 29
What is the problem with recursive locking?
var mu sync.Mutex
func recursive() {
mu.Lock()
recursive() // deadlock: mutex not reentrant
mu.Unlock()
}Question 30
How do you coordinate producer-consumer with WaitGroup?
func producerConsumer() {
var wg sync.WaitGroup
data := make(chan int, 100)
// Producer
wg.Add(1)
go func() {
defer wg.Done()
defer close(data)
for i := 0; i < 10; i++ {
data <- i
}
}()
// Consumer
wg.Add(1)
go func() {
defer wg.Done()
for v := range data {
process(v)
}
}()
wg.Wait()
}Question 31
What is a priority inversion?
Question 32
How do you implement a thread-safe counter with atomic operations?
import "sync/atomic"
var counter int64
func increment() {
atomic.AddInt64(&counter, 1)
}
func get() int64 {
return atomic.LoadInt64(&counter)
}Question 33
What is the convoy effect?
Question 34
How do you handle cleanup with sync.Once?
var once sync.Once
var cleanup func()
func init() {
once.Do(func() {
resource := acquire()
cleanup = func() { release(resource) }
})
}
func Close() {
if cleanup != nil {
cleanup()
}
}Question 35
What is the most important synchronization best practice?
