WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Go · Intermediate · question 23 of 100

What is a goroutine leak? How can you prevent it?

📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

A goroutine leak occurs when a goroutine is created, but there is no way to stop it, resulting in an accumulation of unnecessary goroutines, taking up memory and CPU resources. Such leaks can lead to degraded performance and, in extreme cases, can cause your application to crash due to running out of resources.

To understand this better, let’s consider a simple example. Here’s a function that starts a goroutine with an infinite loop:

func infiniteLoop() {
  go func() {
    for {
      time.Sleep(1 * time.Millisecond)
    }
  }()
}

func main() {
  infiniteLoop()
  time.Sleep(10 * time.Second)
}

The ‘infiniteLoop‘ function starts a goroutine that will keep executing its infinite loop until the main function terminates. This might not be a problem for short-lived programs, but for long-running applications, such goroutines can pile up over time and cause leaks if never terminated.

Preventing goroutine leaks requires ensuring that every created goroutine has a way of terminating gracefully. There are several methods to achieve this, such as using context cancellation, channels, and ‘sync.WaitGroup‘.

Here’s an example using the ‘context‘ package to control goroutine termination:

package main

import (
  "context"
  "fmt"
  "time"
)

func infiniteLoop(ctx context.Context) {
  go func() {
    for {
      select {
      case <-ctx.Done():
        fmt.Println("Goroutine terminated.")
        return
      default:
        time.Sleep(1 * time.Millisecond)
      }
    }
  }()
}

func main() {
  ctx, cancel := context.WithCancel(context.Background())
  infiniteLoop(ctx)
  time.Sleep(10 * time.Second)
  cancel() // cancel the context, which will signal the goroutine to terminate
  time.Sleep(1 * time.Second)
}

In this example, we create a context ‘ctx‘ and pass it to the ‘infiniteLoop‘ function. When the main function sleeps for 10 seconds and then calls ‘cancel()‘, the context’s ‘Done()‘ channel will receive a signal. The goroutine listens for this signal using a ‘select‘ statement and terminates once the context is canceled.

Another way of controlling goroutine termination is using channels. Here’s an example:

package main

import (
  "fmt"
  "time"
)

func infiniteLoop(quitChan chan struct{}) {
  go func() {
    for {
      select {
      case <-quitChan:
        fmt.Println("Goroutine terminated.")
        return
      default:
        time.Sleep(1 * time.Millisecond)
      }
    }
  }()
}

func main() {
  quitChan := make(chan struct{})
  infiniteLoop(quitChan)
  time.Sleep(10 * time.Second)
  close(quitChan) // signal the goroutine to terminate
  time.Sleep(1 * time.Second)
}

Here, we use a ‘quitChan‘ channel to signal the goroutine to terminate. When the main function closes ‘quitChan‘, the goroutine’s ‘select‘ statement receives the signal and terminates the loop.

In conclusion, a goroutine leak occurs when a goroutine is started without a way to terminate it, causing resource consumption issues. To prevent goroutine leaks, ensure that every created goroutine has a controlled way of terminating, such as using context cancellation, channels, or ‘sync.WaitGroup‘. Proper management of goroutines helps maintain the performance and stability of concurrent applications in Go.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Go interview — then scores it.
📞 Practice Go — free 15 min
📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

All 100 Go questions · All topics