У мене є програма, яка викликає метод і передає повернене значення на каналі:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Як я зупиняю таку goroutine?
У мене є програма, яка викликає метод і передає повернене значення на каналі:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Як я зупиняю таку goroutine?
Відповіді:
EDIT: Цю відповідь я написав поспіхом, перш ніж зрозуміти, що ваше питання стосується надсилання значень до чану всередині програми. Підхід нижче можна використовувати або з додатковим чаном, як було запропоновано вище, або використовуючи той факт, що чан, який у вас уже є, є двонаправленим, ви можете використовувати лише один ...
Якщо ваша програма функціонує виключно для обробки елементів, що виходять із чану, ви можете скористатися вбудованим «закритим» і спеціальною формою прийому для каналів.
Тобто, як тільки ви закінчите надсилати елементи на чан, ви закриєте його. Потім всередині вашої програми ви отримуєте додатковий параметр оператору прийому, який показує, чи закритий канал.
Ось повний приклад (група очікувань використовується для того, щоб переконатися, що процес триває до завершення програми goroutut):
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
виклику wg.Done()
, і range ch
цикл перебирає всі значення, поки канал не буде закритий.
Як правило, ви передаєте в програму (можливо, окремий) сигнал-канал. Цей сигнальний канал використовується для введення значення, коли ви хочете, щоб програма зупинилася. Горутіна регулярно опитує цей канал. Як тільки він виявить сигнал, він закривається.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Ви не можете вбити гороутин ззовні. Ви можете сигналізувати гігієнічній програмі про припинення використання каналу, але в рукоятках немає ніякої ручки, щоб виконувати будь-який тип метауправління. Горутини призначені для спільного вирішення проблем, тому вбивство, яке не є поведінкою, майже ніколи не буде адекватною відповіддю. Якщо ви хочете ізоляції для надійності, ви, ймовірно, хочете процесу.
Як правило, ви можете створити канал і отримати сигнал зупинки в програмі.
У цьому прикладі є два способи створення каналу.
канал
контекст . У прикладі я демонструю демонстрацію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")
}
Я знаю, що ця відповідь вже прийнята, але я думав, що я кину свої 2 центи. Мені подобається використовувати гробницю . Це, в основному, канал, який вийшов, але він робить приємні речі, як передавати будь-які помилки. Програма, що контролюється, все ще несе відповідальність за перевірку сигналів віддаленого вбивства. Афаїку неможливо отримати "ідентифікатор" програми і вбити його, якщо він не поводиться (тобто: застряг у нескінченному циклі).
Ось простий приклад, який я перевірив:
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
tomb
робити з програмою у випадку, якщо всередині неї щось трапиться, що викликає паніку? З технічної точки зору, програма в цьому випадку закінчується, тож я припускаю, що вона все ще називатиме відкладеними proc.Tomb.Done()
...
proc.Tomb.Done()
, виконували б до того, як паніка програє програму, але з якою метою? Можливо, що в головній програмі може бути дуже маленьке вікно можливостей для виконання деяких операцій, але вона не має можливості відновитись з паніки в іншій програмі, тому програма все одно виходить з ладу. Документи кажуть: "Коли функція F викликає паніку, виконання F зупиняється, будь-які відкладені функції у F виконуються нормально, а потім F повертається до свого виклику. Процес продовжує стек до тих пір, поки всі функції поточної програми не повернуться, в який момент програма виходить з ладу. "
Особисто я хотів би використовувати діапазон на каналі в програмі:
https://play.golang.org/p/qt48vvDu8cd
Дейв написав чудовий пост про це: http://dave.cheney.net/2013/04/30/curious-channels .