How to PROPERLY stop goroutine? [SOLVED]


Written by - Tuan Nguyen
Reviewed by - Deepak Prasad

In today's article, I will guide you on how to stop a goroutine in Golang. A goroutine is a very lightweight thread that is managed by the Go runtime. Every program in Go has at least one routine called the main Goroutine. A goroutine can be a function or method that runs independently of the main goroutine. A goroutine can only stop by returning from its top-level function; it cannot be forced to suspend by another goroutine.

 

Example 1: Sending a stop signal to the channel

Usually, a channel is passed to the goroutine. When you want the goroutine to stop, you can send a value into the signal channel. The goroutine frequently monitors that channel, it stops when a signal is detected. Let's take a look at the example below to see how we can send a stop signal to a channel to stop a goroutine:

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("This is from main function!")

	quitChan := make(chan bool)
	go func() {
		for {
			select {
			case <-quitChan:
				return
			default:
				// print a message every 3 seconds
				fmt.Println("This is test goroutine")
				time.Sleep(time.Second * 3)
			}
		}
	}()

	// sleep to print some message from the goroutine
	time.Sleep(time.Second * 10)

	// stop the goroutine
	quitChan <- true
	fmt.Println("Go rountine has stopped!")

	// test if the gorountine stopped or not
	time.Sleep(time.Second * 10)
	fmt.Println("This is the end of main func, goroutine no longer run!")
}

Output:

This is from main function!
This is test goroutine
This is test goroutine
This is test goroutine
This is test goroutine
Go rountine has stopped!
This is the end of main func, goroutine no longer run!

 

Example 2: Close the goroutine by closing the channel

When you no longer send items to the channel, you can close it. Inside the goroutine, you can stop the goroutine by checking if the channel is stopped or not. Here is an example of how to stop the goroutine when closing the channel, we can use the waitgroup to make sure the main goroutine will wait until the goroutines stop:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(1)

	strChan := make(chan string)
	go func() {
		for {
			element, ok := <-strChan
			// check if channel is closed
			if !ok {
				println("Goroutines killed!")
				wg.Done()
				return
			}
			println(element)
		}
	}()
	strChan <- "this"
	strChan <- "is"
	strChan <- "a"
	strChan <- "message"
	close(strChan)

	// wait all goroutine to stop
	wg.Wait()
	// print the last message
	fmt.Println("This is the end of main func!")
}

Output:

this
is
a
message
Goroutines killed!
This is the end of main func!

 

Example 3: Close the goroutine by canceling the context

In this example, we will create a channel by using the context. Once the context is closed, the channel will be terminated and the goroutine will be stopped as well. Let's take a look at the example below to see how we can cancel the channel by using the context:

package main

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

func main() {
	// a forever channel
	channel := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())

	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done(): // if cancel() execute
				channel <- struct{}{}
				return
			default:
				fmt.Println("This is from the gorountine")
			}

			// print a message every second
			time.Sleep(1 * time.Second)
		}
	}(ctx)

	// a go routine to close the above context
	go func() {
		time.Sleep(5 * time.Second)
		// close the context
		cancel()
	}()

	// run the gorountine forever
	<-channel
	fmt.Println("This is the end of the main func")
}

Output:

 

Example 4: Using the tomb package to stop a goroutine

The tomb package offers a conventional API for clean goroutine termination. A Tomb tracks the lifecycle of a goroutine as alive, dying, or dead, and the reason for its death. We can install this package by running the following command:

go get gopkg.in/tomb.v1

Here's a simple example that uses the tomb package to kill the goroutine:

package main

import (
	"fmt"
	"time"

	"gopkg.in/tomb.v1"
)

func main() {
	testTomb := tomb.Tomb{}
	go func() {
		defer testTomb.Done() // Must call only once
		for {
			select {
			case <-testTomb.Dying():
				return
			default:
				time.Sleep(1 * time.Second)
				fmt.Println("This is from the goroutine!")
			}
		}
	}()

	time.Sleep(4 * time.Second)
	testTomb.Kill(fmt.Errorf("Death from above"))
	err := testTomb.Wait() // Will return the error that killed the tomb
        time.Sleep(2* time.Second)
	fmt.Println(err)
}

Output:

This is from the goroutine!
This is from the goroutine!
This is from the goroutine!
This is from the goroutine!
Death from above

We can see that after the tomb.Kill() is invoked, the goroutine will no longer print any messages.

 

Summary

In this article, I have introduced several ways to terminate a goroutine. Because we usually use the goroutine with a channel so we can stop the goroutine by stopping the channel. To stop the channel, we can send a stop signal, using the built-in stop or cancel the context that the channel using. Another way to terminate the goroutine is by using the tomb package.

 

References

https://pkg.go.dev/sync
https://pkg.go.dev/context
https://pkg.go.dev/gopkg.in/tomb.v1

 

Tuan Nguyen

He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. You can connect with him on LinkedIn.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment