ฉันมี goroutine ที่เรียกใช้เมธอดและส่งค่าที่ส่งคืนในช่อง:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
ฉันจะหยุด goroutine ได้อย่างไร?
ฉันมี goroutine ที่เรียกใช้เมธอดและส่งค่าที่ส่งคืนในช่อง:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
ฉันจะหยุด goroutine ได้อย่างไร?
คำตอบ:
แก้ไข: ฉันเขียนคำตอบนี้ด้วยความเร่งรีบก่อนที่จะตระหนักว่าคำถามของคุณเกี่ยวกับการส่งค่าไปยังชานภายใน goroutine วิธีการด้านล่างนี้สามารถใช้ได้กับ chan เพิ่มเติมตามที่แนะนำไว้ข้างต้นหรือใช้ข้อเท็จจริงที่ว่า chan ที่คุณมีอยู่แล้วเป็นแบบสองทิศทางคุณสามารถใช้เพียงอันเดียว ...
หากโกรูทีนของคุณมีอยู่เพื่อประมวลผลรายการที่ออกมาจากชานเท่านั้นคุณสามารถใช้ประโยชน์จาก "ปิด" ในตัวและแบบฟอร์มการรับพิเศษสำหรับช่อง
นั่นคือเมื่อคุณส่งรายการบนชานเสร็จแล้วคุณก็จะปิดมัน จากนั้นภายใน goroutine ของคุณคุณจะได้รับพารามิเตอร์พิเศษสำหรับตัวดำเนินการรับที่แสดงว่าช่องถูกปิดหรือไม่
นี่คือตัวอย่างที่สมบูรณ์ (waitgroup ใช้เพื่อให้แน่ใจว่ากระบวนการดำเนินต่อไปจนกว่า 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()
}
defer
เพื่อเรียกwg.Done()
และrange ch
วนซ้ำเพื่อวนซ้ำค่าทั้งหมดจนกว่าช่องจะปิด
โดยปกติแล้วคุณจะส่งผ่านช่องสัญญาณ goroutine (อาจแยกจากกัน) ช่องสัญญาณนั้นใช้เพื่อดันค่าเมื่อคุณต้องการให้ goroutine หยุด goroutine โพลช่องนั้นเป็นประจำ ทันทีที่ตรวจพบสัญญาณจะหยุดทำงาน
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
คุณไม่สามารถฆ่าโกรูทีนจากภายนอกได้ คุณสามารถส่งสัญญาณให้ goroutine หยุดใช้ช่องสัญญาณได้ แต่ไม่มีการจัดการกับ goroutines ในการจัดการเมตาใด ๆ Goroutines มีจุดมุ่งหมายเพื่อแก้ปัญหาร่วมกันดังนั้นการฆ่าคนที่ประพฤติไม่ดีแทบจะไม่เป็นการตอบสนองที่เพียงพอ หากคุณต้องการแยกเพื่อความแข็งแรงคุณอาจต้องการกระบวนการ
โดยทั่วไปคุณสามารถสร้างช่องสัญญาณและรับสัญญาณหยุดใน goroutine
มีสองวิธีในการสร้างช่องในตัวอย่างนี้
ช่อง
สิ่งแวดล้อม ในตัวอย่างฉันจะสาธิต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 เซ็นต์เข้าไปฉันชอบใช้แพ็คเกจหลุมฝังศพ โดยพื้นฐานแล้วมันเป็นช่องทางการออกจากระบบ แต่มันก็ทำได้ดีเช่นส่งคืนข้อผิดพลาดใด ๆ เช่นกัน กิจวัตรภายใต้การควบคุมยังคงมีหน้าที่ในการตรวจสอบสัญญาณการฆ่าระยะไกล Afaik เป็นไปไม่ได้ที่จะรับ "id" ของ goroutine และฆ่ามันถ้ามันทำงานผิดปกติ (เช่น: ติดอยู่ในวงวนที่ไม่มีที่สิ้นสุด)
นี่คือตัวอย่างง่ายๆที่ฉันทดสอบ:
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
goroutine ทำอะไรได้บ้างในกรณีที่มีบางสิ่งเกิดขึ้นภายในที่ทำให้ตกใจเช่น? ในทางเทคนิคแล้ว goroutine ออกในกรณีนี้ดังนั้นฉันคิดว่ามันจะยังคงเรียกว่ารอการตัดบัญชีproc.Tomb.Done()
...
proc.Tomb.Done()
จะดำเนินการก่อนที่โปรแกรมจะล่ม แต่จะจบลงอย่างไร เป็นไปได้ว่า goroutine หลักอาจมีโอกาสน้อยมากในการดำเนินการบางคำสั่ง แต่ก็ไม่มีทางฟื้นตัวจากความตื่นตระหนกใน goroutine อื่นได้ดังนั้นโปรแกรมจึงยังคงหยุดทำงาน เอกสารกล่าวว่า: "เมื่อฟังก์ชัน F เรียกความตื่นตระหนกการดำเนินการของ F จะหยุดลงฟังก์ชันใด ๆ ที่รอการตัดบัญชีใน F จะดำเนินการตามปกติจากนั้น F จะส่งกลับไปยังผู้เรียก .. กระบวนการจะดำเนินต่อไปในกองซ้อนจนกว่าฟังก์ชันทั้งหมดใน goroutine ปัจจุบันจะกลับมา เมื่อถึงจุดที่โปรแกรมขัดข้อง "
โดยส่วนตัวแล้วฉันต้องการใช้ range ในช่องใน goroutine:
https://play.golang.org/p/qt48vvDu8cd
เดฟได้เขียนโพสต์มากเกี่ยวกับเรื่องนี้: http://dave.cheney.net/2013/04/30/curious-channels