Bir yöntem çağıran ve bir kanalda döndürülen değeri ileten bir gorutinim var:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Böyle bir gorutini nasıl durdururum?
Bir yöntem çağıran ve bir kanalda döndürülen değeri ileten bir gorutinim var:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Böyle bir gorutini nasıl durdururum?
Yanıtlar:
DÜZENLEME: Sorunuzun bir gorutinin içindeki bir kanala değerler göndermekle ilgili olduğunu anlamadan önce bu yanıtı aceleyle yazdım. Aşağıdaki yaklaşım, yukarıda önerildiği gibi ek bir kanal ile kullanılabilir veya sahip olduğunuz kanalın çift yönlü olduğu gerçeğini kullanarak, sadece birini kullanabilirsiniz ...
Programınız yalnızca kanaldan çıkan öğeleri işlemek için mevcutsa, "kapat" yerleşikini ve kanallar için özel alma formunu kullanabilirsiniz.
Yani, kanala öğe göndermeyi tamamladığınızda kapatırsınız. Daha sonra, programınızın içinde, alma operatörüne kanalın kapatılıp kapatılmadığını gösteren ekstra bir parametre alırsınız.
İşte eksiksiz bir örnek (bekleme grubu, işlemin gorutin tamamlanana kadar devam etmesini sağlamak için kullanılır):
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
olarak çağrı yapmak ve kanal kapanana kadar tüm değerler üzerinde yinelemek için wg.Done()
bir range ch
döngü kullanılarak yazılır .
Tipik olarak, gorutine (muhtemelen ayrı) bir sinyal kanalı iletirsiniz. Bu sinyal kanalı, gorutinin durmasını istediğinizde bir değeri içeri itmek için kullanılır. Goroutin, düzenli olarak bu kanalda anketler yapar. Bir sinyal algıladığı anda kapanır.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Bir gorutini dışarıdan öldüremezsin. Bir gorutinin bir kanalı kullanmayı bırakması için sinyal verebilirsiniz, ancak gorutinlerin herhangi bir meta yönetimi yapmaları için bir yolu yoktur. Goroutinlerin problemleri işbirliği içinde çözmesi amaçlanmıştır, bu nedenle yanlış davranan birini öldürmek neredeyse hiçbir zaman yeterli bir cevap olmayacaktır. Sağlamlık için izolasyon istiyorsanız, muhtemelen bir süreç istersiniz.
Genel olarak, bir kanal oluşturabilir ve gorutinde bir durdurma sinyali alabilirsiniz.
Bu örnekte kanal oluşturmanın iki yolu var.
kanal
bağlam . Örnekte demo yapacağımcontext.WithCancel
İlk demo, şunu kullanın 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")
}
İkinci demo, şunu kullanın 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")
}
Bu cevabın zaten kabul edildiğini biliyorum ama 2 sentimi de atayım dedim. Mezar paketini kullanmayı seviyorum . Temelde yenilenmiş bir çıkış kanalı, ancak herhangi bir hatayı geri almak gibi güzel şeyler de yapıyor. Kontrol altındaki rutin hala uzaktan öldürme sinyallerini kontrol etme sorumluluğuna sahiptir. Afaik, bir gorutinin "id" sini almak ve yanlış davranıyorsa onu öldürmek mümkün değildir (yani: sonsuz bir döngüde sıkışmışsa).
İşte test ettiğim basit bir örnek:
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)
}
Çıktı şöyle görünmelidir:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
Örneğin, içinde panik yaratan bir şey olması durumunda gorutinin ne yapacağını test ettiniz mi? Teknik olarak konuşursak, gorutin bu durumda çıkıyor, bu yüzden hala ertelenmiş olarak adlandıracağını varsayıyorum proc.Tomb.Done()
...
proc.Tomb.Done()
, panik programı çökertmeden çalıştırırdı, ama ne amaçla? Ana gorutinin bazı ifadeleri yürütmek için çok küçük bir fırsat penceresine sahip olması mümkündür , ancak başka bir gorutindeki panikten kurtulmanın bir yolu yoktur, bu nedenle program hala çökmektedir. Dokümanlar şöyle der: "F fonksiyonu panik çağırdığında, F'nin yürütülmesi durur, F'deki ertelenmiş tüm fonksiyonlar normal olarak yürütülür ve ardından F, çağırana geri döner ... İşlem, mevcut programdaki tüm fonksiyonlar geri dönene kadar yığını sürdürür, bu noktada program çöküyor. "
Şahsen, bir gorutindeki bir kanalda menzili kullanmak istiyorum:
https://play.golang.org/p/qt48vvDu8cd
Dave bununla ilgili harika bir gönderi yazdı: http://dave.cheney.net/2013/04/30/curious-channels .