7

I'm trying to stop a go routine but I can't find a way to achieve this. I was thinking to use a 2nd channel but if I read from that it would block it isn't it ?. Here is some code which I hope explains what I'm trying to do.

package main

import "fmt"
import "time"

func main() {

    var tooLate bool

    proCh := make(chan string)

    go func() {
        for {
               fmt.Println("working")
        //if is tooLate we stop/return it
            if tooLate { 
            fmt.Println("stopped")
                return
            }
       //processing some data and send the result on proCh
            time.Sleep(2 * time.Second)
            proCh <- "processed"
            fmt.Println("done here")

        }
    }()
    select {
    case proc := <-proCh:
        fmt.Println(proc)
    case <-time.After(1 * time.Second):
        // somehow send tooLate <- true
        //so that we can stop the go routine running
        fmt.Println("too late")
    }

    time.Sleep(4 * time.Second)
    fmt.Println("finish\n")
}

Play this thing

Anthony Hunt
  • 1,470
  • 5
  • 20
  • 32
  • Possible duplicate of http://stackoverflow.com/questions/6807590/how-to-stop-a-goroutine – jimt Sep 30 '14 at 14:01

1 Answers1

2

There are few ways to achive that, the easiest and most convenient is using another channel like:

func main() {
    tooLate := make(chan struct{})
    proCh := make(chan string)

    go func() {
        for {
            fmt.Println("working")
            time.Sleep(1 * time.Second)
            select {
            case <-tooLate:
                fmt.Println("stopped")
                return
            case proCh <- "processed": //this why it won't block the goroutine if the timer expirerd.
            default: // adding default will make it not block
            }
            fmt.Println("done here")

        }
    }()
    select {
    case proc := <-proCh:
        fmt.Println(proc)
    case <-time.After(1 * time.Second):
        fmt.Println("too late")
        close(tooLate)
    }

    time.Sleep(4 * time.Second)
    fmt.Println("finish\n")
}

playground

You can also look into using sync.Cond

OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • why do we need ``case proCh <- "processed":`` on the 2nd select case from the first go routine? it would return if the timer expired due ``tooLate <- true`` so it's impossible to have that case isn't it ? – Anthony Hunt Sep 30 '14 at 14:00
  • @AnthonyHat no, because after `case <-time.After(1 * time.Second)` your goroutine will get stuck trying to `proCh <- "processed"` and it will never unblock until the program exits. – OneOfOne Sep 30 '14 at 14:55
  • @OneOfOne if the timer expired the goroutine simply returns ``case <-tooLate: fmt.Println("stopped") return`` It doesn't seem to block, nor I do see any reason. http://play.golang.org/p/MvmjBXfAqV – Anthony Hunt Oct 01 '14 at 06:13
  • @AnthonyHat the `default:` in select makes it nonblocking, also when you commented `close(tooLate)` you made it so `case <-tooLate` will never select. – OneOfOne Oct 01 '14 at 06:23
  • Why make the chan type Struct{} ? –  Mar 27 '15 at 11:29
  • @Atomic_alarm because it uses the least memory, `struct{}` is a special case for most data structs in go, it uses the least amount of memory. – OneOfOne Mar 28 '15 at 16:50