By Ɓukasz Gruner


2011-07-24 15:05:03 8 Comments

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?

6 comments

@zouying 2017-11-15 08:47:06

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")
}

@laslowh 2011-11-11 19:19:11

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()
}

@Alan Donovan 2015-07-16 13:53:00

The body of the inner goroutine is more idiomatically written using defer to call wg.Done(), and a range ch loop to iterate over all values until the channel is closed.

@mikespook 2014-05-14 06:05:02

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

http://play.golang.org/p/KjG8FLzPoz

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

@Profpatsch 2014-11-24 09:35:58

That is truly beautiful.

@Kevin Cantwell 2013-11-23 18:34:01

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

@Gwyneth Llewelyn 2017-08-11 17:51:04

This package is quite interesting! Have you tested to see what tomb does with the goroutine in case something happens inside it that throws a panic, for instance? Technically speaking, the goroutine exits in this case, so I'm assuming it will still call the deferred proc.Tomb.Done()...

@Kevin Cantwell 2017-09-06 19:05:54

Hi Gwyneth, yes proc.Tomb.Done() would execute before the panic crashes the program, but to what end? It's possible that the main goroutine may have a very small window of opportunity to execute some statements, but it has no way of recovering from a panic in another goroutine, so the program still crashes. Docs say: "When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller..The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes."

@SteveMcQwark 2011-07-28 07:40:51

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.

@Jeff Allen 2012-08-28 14:45:32

And you might want to look into the encoding/gob package, which would let two Go programs easily exchange data structures over a pipe.

@Omnifarious 2018-02-06 19:44:10

In my case, I have a goroutine that will be blocked on a system call, and I need to tell it to abort the system call and then exit. If I were blocked on a channel read, it would be possible to do as you suggest.

@rouzier 2018-05-09 13:54:09

I saw that issue before. The way we "solved" it was to increase the number of threads in the start of the application to match the number of goroutines that could possibly + the number of CPUs

@jimt 2011-07-24 15:39:24

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

@Elazar Leibovich 2011-07-26 11:39:08

Not good enough. What if the goroutine is stuck in an endless loop, due to a bug?

@jimt 2011-07-26 20:51:01

Then the bug should be fixed.

@Jeremy Wall 2011-07-30 05:23:57

Elazar, What you suggest is a way to stop a function after you've called it. A goroutine is not a thread. It may run in a different thread or it may run in the same thread as yours. I know of no language that supports what you seem to think Go should support.

@MatthewToday 2011-07-31 01:36:44

@jeremy Not disagreeing for Go, but Erlang allows you to kill a process that is running a looping function.

@Jeff Allen 2012-08-28 14:42:36

Go multitasking is cooperative, not preemptive. A goroutine in a loop never enters the scheduler, so it can never be killed.

@AntoineG 2014-05-20 06:12:49

You'd be better closing the channel, this way any listener (not only the first to receive from quit) would receive the quit signal.

@Greg 2014-05-27 23:25:35

@Jeff This was correct when you wrote it I believe; Go used to schedule when certain blocking calls were made. Now, Go will potentially schedule during function calls(it's actually very interesting how this is implemented and maintains performance). So if the loop makes function calls it could invoke the scheduler. If not, it won't :)

@Donn Lee 2016-05-05 17:21:49

If you decide to close the channel there's at least one difference: You might need to give the goroutine time to finish its "Do other stuff" before main() ends. I created a playground that demonstrates this: play.golang.org/p/EdL3fdxoBp

@Bora M. Alper 2017-07-19 09:25:39

@jimt I wish I could downvote your comment (although, +1 for the useful answer). What Elazar has said has some merit in it, though it might be wrongly worded: how can we stop a goroutine that is busy, that is the question. Why shake off someone when it could be explained perfectly with respect to the go's concurrency model, which Jeremy's answer made perfectly clear to me. There is no need for "charismatic" answers when the other person asks for an explanatory one.

@se_brandon 2017-10-16 21:10:31

Is there anyway to "finish" the code in the "default" case? Like, I want to issue the quit, but still keep processing any existing work.

@swcraft 2019-06-08 17:02:49

@BoraM.Alper and Jeremy, I have to be with Elazar because Thread.Kill() or now supported CancellationToken() in C# is what exactly for situation Elazar questioned. I wish there was a way when never ending accidental bug occurrs, then send down signal to forcekill and collect garbage.

Related Questions

Sponsored Content

3 Answered Questions

[SOLVED] Catching return values from goroutines

1 Answered Questions

[SOLVED] How to stop one of multilpe of the same goroutine

  • 2019-03-19 17:22:19
  • vinni_f
  • 50 View
  • 0 Score
  • 1 Answer
  • Tags:   go goroutine

1 Answered Questions

[SOLVED] How to stop a goroutine if we fail to listen from a channel passed to that routine

  • 2019-02-01 08:22:53
  • Rahul Kushwaha
  • 49 View
  • -1 Score
  • 1 Answer
  • Tags:   go

1 Answered Questions

The Behaviour Of Goroutines with Channels

1 Answered Questions

2 Answered Questions

[SOLVED] How to stop a goroutine that is listening for RethinkDB changefeeds?

1 Answered Questions

[SOLVED] How to kill goroutines?

  • 2018-08-21 03:23:07
  • André Betz
  • 319 View
  • 3 Score
  • 1 Answer
  • Tags:   go goroutine

5 Answered Questions

[SOLVED] Multiple goroutines listening on one channel

  • 2013-03-30 06:14:10
  • Ilia Choly
  • 44777 View
  • 61 Score
  • 5 Answer
  • Tags:   go

3 Answered Questions

[SOLVED] How to stop goroutine

  • 2017-02-28 19:00:39
  • user5646735
  • 2622 View
  • 4 Score
  • 3 Answer
  • Tags:   go goroutine

1 Answered Questions

[SOLVED] Is there a better way to stop an infinite goroutine in Go?

  • 2012-06-20 15:36:05
  • Matt
  • 4862 View
  • 3 Score
  • 1 Answer
  • Tags:   go goroutine

Sponsored Content