How to stop a goroutine

GoGoroutineChannels

Go Problem Overview


I have a goroutine that calls a method, and passes returned value on a channel:

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()

How do I stop such a goroutine?

Go Solutions


Solution 1 - Go

Typically, you pass the goroutine a (possibly separate) signal channel. That signal channel is used to push a value into when you want the goroutine to stop. The goroutine polls that channel regularly. As soon as it detects a signal, it quits.

quit := make(chan bool)
go func() {
    for {
        select {
        case <- quit:
            return
        default:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
quit <- true

Solution 2 - Go

EDIT: I wrote this answer up in haste, before realizing that your question is about sending values to a chan inside a goroutine. The approach below can be used either with an additional chan as suggested above, or using the fact that the chan you have already is bi-directional, you can use just the one...

If your goroutine exists solely to process the items coming out of the chan, you can make use of the "close" builtin and the special receive form for channels.

That is, once you're done sending items on the chan, you close it. Then inside your goroutine you get an extra parameter to the receive operator that shows whether the channel has been closed.

Here is a complete example (the waitgroup is used to make sure that the process continues until the goroutine completes):

package main

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

	ch := make(chan int)
	go func() {
		for {
			foo, ok := <- ch
			if !ok {
				println("done")
				wg.Done()
				return
			}
			println(foo)
		}
	}()
	ch <- 1
	ch <- 2
	ch <- 3
	close(ch)

	wg.Wait()
}

Solution 3 - Go

Generally, you could create a channel and receive a stop signal in the goroutine.

There two way to create channel in this example.

  1. channel

  2. context. In the example I will demo context.WithCancel

The first demo, use channel:

package main

import "fmt"
import "time"

func do_stuff() int {
	return 1
}

func main() {

	ch := make(chan int, 100)
	done := make(chan struct{})
	go func() {
		for {
			select {
			case ch <- do_stuff():
			case <-done:
				close(ch)
				return
			}
			time.Sleep(100 * time.Millisecond)
		}
	}()

	go func() {
		time.Sleep(3 * time.Second)
		done <- struct{}{}
	}()

	for i := range ch {
		fmt.Println("receive value: ", i)
	}

	fmt.Println("finish")
}

The second demo, use context:

package main

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

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

	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():  // if cancel() execute
				forever <- struct{}{}
                return
			default:
				fmt.Println("for loop")
			}

			time.Sleep(500 * time.Millisecond)
		}
	}(ctx)

	go func() {
		time.Sleep(3 * time.Second)
		cancel()
	}()

	<-forever
	fmt.Println("finish")
}

Solution 4 - Go

You can't kill a goroutine from outside. You can signal a goroutine to stop using a channel, but there's no handle on goroutines to do any sort of meta management. Goroutines are intended to cooperatively solve problems, so killing one that is misbehaving would almost never be an adequate response. If you want isolation for robustness, you probably want a process.

Solution 5 - Go

I know this answer has already been accepted, but I thought I'd throw my 2cents in. I like to use the tomb package. It's basically a suped up quit channel, but it does nice things like pass back any errors as well. The routine under control still has the responsibility of checking for remote kill signals. Afaik it's not possible to get an "id" of a goroutine and kill it if it's misbehaving (ie: stuck in an infinite loop).

Here's a simple example which I tested:

package main

import (
  "launchpad.net/tomb"
  "time"
  "fmt"
)

type Proc struct {
  Tomb tomb.Tomb
}

func (proc *Proc) Exec() {
  defer proc.Tomb.Done() // Must call only once
  for {
    select {
    case <-proc.Tomb.Dying():
      return
    default:
      time.Sleep(300 * time.Millisecond)
      fmt.Println("Loop the loop")
    }
  }
}

func main() {
  proc := &Proc{}
  go proc.Exec()
  time.Sleep(1 * time.Second)
  proc.Tomb.Kill(fmt.Errorf("Death from above"))
  err := proc.Tomb.Wait() // Will return the error that killed the proc
  fmt.Println(err)
}

The output should look like:

# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above

Solution 6 - Go

Personally, I'd like to use range on a channel in a goroutine:

https://play.golang.org/p/qt48vvDu8cd

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	c := make(chan bool)
	wg.Add(1)
	go func() {
		defer wg.Done()
		for b := range c {
			fmt.Printf("Hello %t\n", b)
		}
	}()
	c <- true
	c <- true
	close(c)
	wg.Wait()
}

Dave has written a great post about this: http://dave.cheney.net/2013/04/30/curious-channels.

Solution 7 - Go

I am going to offer a slightly different approach than the ones provided here.

I am going to assume the goroutine that needs to be stopped is performing some work that is not related at all to other goroutines. That work will be represented by the default select case:

default:
    fmt.Println("working")
    time.Sleep(1 * time.Second)

Another goroutine (in my example will be the main) decides that it should stop the goroutine that is performing some work. You cannot really kill the goroutine. Even if you could it would be a bad idea because it could leave the goroutine in an undesired state. So, we have to use a channel to communicate that someone is signaling to the goroutine to stop.

stop := make(chan struct{})

Since the goroutine will be continuously performing some work. We will use a loop to represent that. And when the stop signal is sent, the goroutine breaks out of the loop.

go func() {
L:
	for {
		select {
		case <-stop:
			fmt.Println("stopping")
			break L
		default:
			fmt.Println("working")
			time.Sleep(1 * time.Second)
		}
	}
}()

We can use another channel to indicate to the main that the goroutine has stopped. Here's the full example:

package main

import (
	"fmt"
	"time"
)

func main() {
	stop := make(chan struct{})
	stopped := make(chan struct{})

	go func() {
	L:
		for {
			select {
			case <-stop:
				fmt.Println("stopping")
				break L
			default:
				fmt.Println("working")
				time.Sleep(1 * time.Second)
			}
		}

		fmt.Println("stopped")
		stopped <- struct{}{}
	}()

	<-time.After(5 * time.Second)
	stop <- struct{}{} // send a signal to stop
	close(stop)
	<-stopped // wait for stop
}

The main thread spawns a goroutine to perform some work for some time (in this case 5 seconds). When the time expires, it sends a stop signal to the goroutine and waits for it until the goroutine is fully stopped.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionŁukasz GrunerView Question on Stackoverflow
Solution 1 - GojimtView Answer on Stackoverflow
Solution 2 - GolaslowhView Answer on Stackoverflow
Solution 3 - GozouyingView Answer on Stackoverflow
Solution 4 - GoSteveMcQwarkView Answer on Stackoverflow
Solution 5 - GoKevin CantwellView Answer on Stackoverflow
Solution 6 - GomikespookView Answer on Stackoverflow
Solution 7 - GoBruno CalzaView Answer on Stackoverflow