Saya memiliki goroutine yang memanggil metode, dan meneruskan nilai yang dikembalikan pada saluran:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Bagaimana cara menghentikan goroutine seperti itu?
Saya memiliki goroutine yang memanggil metode, dan meneruskan nilai yang dikembalikan pada saluran:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Bagaimana cara menghentikan goroutine seperti itu?
Jawaban:
EDIT: Saya menulis jawaban ini dengan tergesa-gesa, sebelum menyadari bahwa pertanyaan Anda adalah tentang mengirim nilai ke chan di dalam goroutine. Pendekatan di bawah ini dapat digunakan baik dengan chan tambahan seperti yang disarankan di atas, atau menggunakan fakta bahwa chan yang sudah Anda miliki adalah dua arah, Anda dapat menggunakan salah satu ...
Jika goroutine Anda ada hanya untuk memproses item yang keluar dari chan, Anda dapat menggunakan builtin "tutup" dan formulir terima khusus untuk saluran.
Artinya, setelah Anda selesai mengirim barang di chan, Anda menutupnya. Kemudian di dalam goroutine Anda, Anda mendapatkan parameter tambahan ke operator penerima yang menunjukkan apakah saluran telah ditutup.
Berikut adalah contoh lengkapnya (grup tunggu digunakan untuk memastikan bahwa proses berlanjut hingga goroutine selesai):
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()
}
defer
to call wg.Done()
, dan range ch
loop untuk mengulang semua nilai hingga saluran ditutup.
Biasanya, Anda meneruskan saluran sinyal goroutine (mungkin terpisah). Saluran sinyal itu digunakan untuk mendorong nilai saat Anda ingin goroutine berhenti. Goroutine memilih saluran tersebut secara teratur. Begitu mendeteksi sinyal, itu berhenti.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Anda tidak bisa membunuh goroutine dari luar. Anda bisa memberi tanda pada goroutine untuk berhenti menggunakan saluran, tetapi tidak ada pegangan pada goroutine untuk melakukan manajemen meta apa pun. Goroutine dimaksudkan untuk memecahkan masalah secara kooperatif, jadi membunuh seseorang yang berperilaku tidak baik hampir tidak akan pernah menjadi respons yang memadai. Jika Anda menginginkan isolasi untuk ketahanan, Anda mungkin menginginkan sebuah proses.
Umumnya, Anda dapat membuat saluran dan menerima sinyal berhenti di goroutine.
Ada dua cara untuk membuat saluran dalam contoh ini.
saluran
konteks . Dalam contoh ini saya akan democontext.WithCancel
Demo pertama, gunakan 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")
}
Demo kedua, gunakan 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")
}
Saya tahu jawaban ini telah diterima, tetapi saya pikir saya akan membuang 2 sen saya. Saya suka menggunakan paket makam . Ini pada dasarnya adalah saluran keluar yang ditambahkan, tetapi itu melakukan hal-hal baik seperti mengembalikan kesalahan apa pun juga. Rutinitas yang dikendalikan masih memiliki tanggung jawab untuk memeriksa sinyal mematikan jarak jauh. Afaik tidak mungkin mendapatkan "id" dari sebuah goroutine dan membunuhnya jika ia berperilaku tidak semestinya (yaitu: terjebak dalam loop tak terbatas).
Berikut contoh sederhana yang saya uji:
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)
}
Outputnya akan terlihat seperti:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
dilakukan dengan goroutine seandainya terjadi sesuatu di dalamnya yang menimbulkan kepanikan, misalnya? Secara teknis, goroutine keluar dalam kasus ini, jadi saya berasumsi itu masih akan memanggil deferred proc.Tomb.Done()
...
proc.Tomb.Done()
akan mengeksekusi sebelum kepanikan merusak program, tetapi untuk tujuan apa? Mungkin saja goroutine utama memiliki jendela kesempatan yang sangat kecil untuk mengeksekusi beberapa pernyataan, tetapi tidak ada cara untuk memulihkan dari kepanikan di goroutine lain, sehingga program tetap macet. Docs mengatakan: "Ketika fungsi F memanggil panic, eksekusi F berhenti, semua fungsi yang ditangguhkan di F dijalankan secara normal, dan kemudian F kembali ke pemanggilnya..Proses terus menumpuk sampai semua fungsi di goroutine saat ini telah kembali, pada titik mana program macet. "
Secara pribadi, saya ingin menggunakan range pada saluran di goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave telah menulis postingan yang bagus tentang ini: http://dave.cheney.net/2013/04/30/curious-channels .