如何阻止goroutine

dba5bblo  于 2023-02-17  发布在  Go
关注(0)|答案(9)|浏览(104)

我有一个goroutine,它调用一个方法,并在一个通道上传递返回值:

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

我该怎么阻止这样一个goroutine呢?

beq87vna

beq87vna1#

一般来说,你会给goroutine传递一个(可能是单独的)信号通道,这个信号通道用来在你想要goroutine停止的时候把一个值推到里面,goroutine会定期轮询这个通道,一旦检测到信号,它就会退出。

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

// Do stuff

// Quit goroutine
quit <- true
vojdkbi0

vojdkbi02#

**EDIT:**我匆忙地写下了这个答案,然后才意识到你的问题是关于发送值到goroutine中的chan,下面的方法既可以和上面建议的额外chan一起使用,也可以利用你已经拥有的chan是双向的这一事实,你可以只使用一个......

如果goroutine的存在仅仅是为了处理chan中的元素,那么你可以使用内置的“close”和特殊的接收表单。
也就是说,一旦你发送完了chan上的项目,你就关闭它,然后在goroutine中,你会得到一个额外的参数给receive操作符,用来显示通道是否已经关闭。
下面是一个完整的例子(等待组用来确保进程一直持续到goroutine完成):

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

6ljaweal3#

一般来说,你可以在goroutine中创建一个通道并接收一个停止信号。
在本例中有两种创建频道方法。
1.渠道
1.上下文。在示例中,我将演示context.WithCancel
第一个演示使用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")
}

第二个演示使用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")
}
wdebmtf2

wdebmtf24#

你不能从外部杀死一个goroutine,你可以给它发信号让它停止使用一个通道,但是goroutine上没有句柄来做任何类型的 meta管理,goroutine是用来合作解决问题的,所以杀死一个行为不端的goroutine几乎永远都不是一个合适的回应,如果你想要隔离以获得健壮性,你可能需要一个进程。

2j4z5cfb

2j4z5cfb5#

我知道这个答案已经被接受了,但是我想我还是要把我的2美分投入进去。我喜欢使用tomb包。它基本上是一个suped up退出通道,但是它也做了一些很好的事情,比如回传错误。在控制下的例程仍然有检查远程终止信号的责任。但是,如果一个goroutine的“id”被删除了,那是不可能的。行为不端(即:陷入无限循环)。
下面是我测试过的一个简单的例子:

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

输出应如下所示:

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

tzxcd3kk6#

就我个人而言,我更喜欢在goroutine的通道中使用range:
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()
}

戴夫写了一个伟大的职位关于这一点:http://dave.cheney.net/2013/04/30/curious-channels.

iqih9akk

iqih9akk7#

我将提供一种与这里提供的方法略有不同的方法。
我假设需要停止的goroutine正在执行一些与其他goroutines完全无关的工作,这些工作将由default select case表示:

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

另一个goroutine(在我的例子中是main)决定停止正在执行某些工作的goroutine。您不能真正终止goroutine。即使您可以终止goroutine,这也是一个坏主意,因为它可能使goroutine处于不希望的状态。因此,我们必须使用信道来传达有人正在向goroutine发信号以停止。

stop := make(chan struct{})

由于goroutine会持续执行一些工作,我们将使用循环来表示,当停止信号发出时,goroutine会跳出循环。

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

我们可以使用另一个通道来通知main goroutine已经停止,下面是完整的例子:

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
}

main线程派生一个goroutine执行某项工作一段时间(在本例中为5秒),当时间到期时,它向goroutine发送停止信号,并等待它,直到goroutine完全停止。

bxjv4tth

bxjv4tth8#

我修改了邹英的答案,使之成为一个可停止的计时器。希望它能帮助其他人。

func main() {

    timeOut := make(chan bool)
    stopTimer := false

    //10 sec timer - go-routine
    go func() {
        var counter = 0
        for {
            if stopTimer{
                timeOut <- false
                return
            }else {
                fmt.Printf("timer: %d sec\n", counter)
                time.Sleep(1 * time.Second)
                counter++
                if counter >= 10 {
                    timeOut <- true
                    return
                }
            } 
        }
    }()

    go func() {
        time.Sleep(8 * time.Second) //change 8 to 11 or more to time-out the timer
        stopTimer = true
    }()

    b := <-timeOut
    fmt.Printf("time-out ,%v\n", b)

}
pdkcd3nj

pdkcd3nj9#

我使用close(quitCh)执行以下操作:使用close(),它将广播到所有监听通道以退出,在本例中,所有go例程都在监听退出ch

package main

import (
    "fmt"
    "sync"
    "time"
)

func routine(ch chan struct{}, wg *sync.WaitGroup, id int) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ch:
            wg.Done()
            fmt.Println(id, "quiting")
            return
        case <-ticker.C:
            fmt.Println(id, "do your stuff")
        }
    }

}

func main() {

    var wg sync.WaitGroup

    c := make(chan struct{}, 1)
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go routine(c, &wg, i)
    }

    <-time.After(time.Second * 2)
    close(c)
    fmt.Println("waiting")
    wg.Wait()

    fmt.Println("Done")

}

相关问题