Tôi có một quy trình gọi một phương thức và chuyển giá trị trả về trên một kênh:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Làm cách nào để dừng một thói quen như vậy?
Tôi có một quy trình gọi một phương thức và chuyển giá trị trả về trên một kênh:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Làm cách nào để dừng một thói quen như vậy?
Câu trả lời:
CHỈNH SỬA: Tôi đã viết câu trả lời này một cách vội vàng, trước khi nhận ra rằng câu hỏi của bạn là về việc gửi các giá trị đến một chan bên trong một quy trình. Cách tiếp cận dưới đây có thể được sử dụng với một chan bổ sung như đã đề xuất ở trên, hoặc sử dụng thực tế là chan bạn đã có hai hướng, bạn có thể chỉ sử dụng một ...
Nếu quy trình goroutine của bạn chỉ tồn tại để xử lý các mặt hàng ra khỏi chan, bạn có thể sử dụng nội trang "đóng" và biểu mẫu nhận đặc biệt cho các kênh.
Tức là sau khi gửi xong các mục trên chan, bạn đóng nó lại. Sau đó, bên trong quy trình của bạn, bạn nhận được một tham số bổ sung cho toán tử nhận cho biết liệu kênh đã bị đóng hay chưa.
Đây là một ví dụ đầy đủ (nhóm chờ được sử dụng để đảm bảo rằng quá trình tiếp tục cho đến khi quy trình hoàn tất):
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
để gọi wg.Done()
và một range ch
vòng lặp để lặp lại tất cả các giá trị cho đến khi kênh bị đóng.
Thông thường, bạn chuyển goroutine một kênh tín hiệu (có thể riêng biệt). Kênh tín hiệu đó được sử dụng để đẩy một giá trị vào khi bạn muốn quy trình dừng lại. Goroutine thăm dò kênh đó thường xuyên. Ngay sau khi phát hiện một tín hiệu, nó sẽ thoát ra.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Bạn không thể giết một quy trình từ bên ngoài. Bạn có thể báo hiệu một quy trình ngừng sử dụng một kênh, nhưng không có quy trình nào về quy trình thực hiện bất kỳ loại quản lý meta nào. Goroutines nhằm mục đích hợp tác giải quyết các vấn đề, do đó, việc giết một con đang hoạt động sai hầu như không bao giờ là một phản ứng thích hợp. Nếu bạn muốn sự cô lập để có sự mạnh mẽ, bạn có thể muốn có một quá trình.
Nói chung, bạn có thể tạo kênh và nhận tín hiệu dừng trong quy trình.
Có hai cách để tạo kênh trong ví dụ này.
kênh
bối cảnh . Trong ví dụ, tôi sẽ democontext.WithCancel
Bản demo đầu tiên, sử dụng 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")
}
Bản demo thứ hai, sử dụng 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")
}
Tôi biết câu trả lời này đã được chấp nhận, nhưng tôi nghĩ tôi sẽ ném 2 xu của mình vào. Tôi thích sử dụng gói mộ . Về cơ bản, nó là một kênh thoát đã hoàn thành, nhưng nó cũng thực hiện những điều tốt đẹp như trả lại bất kỳ lỗi nào. Quy trình được kiểm soát vẫn có trách nhiệm kiểm tra các tín hiệu tiêu diệt từ xa. Afaik không thể lấy "id" của một quy trình và giết nó nếu nó hoạt động sai (tức là: bị mắc kẹt trong một vòng lặp vô hạn).
Đây là một ví dụ đơn giản mà tôi đã thử nghiệm:
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)
}
Đầu ra sẽ giống như sau:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
gì xảy ra với goroutine trong trường hợp có điều gì đó xảy ra bên trong nó khiến bạn hoảng sợ chưa? Về mặt kỹ thuật, các lối ra goroutine trong trường hợp này, vì vậy tôi giả định nó sẽ vẫn gọi trì hoãn proc.Tomb.Done()
...
proc.Tomb.Done()
sẽ thực thi trước khi chương trình hoảng sợ, nhưng kết thúc là gì? Có thể quy trình chính có thể có một cơ hội rất nhỏ để thực thi một số câu lệnh, nhưng nó không có cách nào khôi phục sau cơn hoảng loạn trong một quy trình khác, vì vậy chương trình vẫn bị treo. Docs cho biết: "Khi hàm F gọi hoảng loạn, việc thực thi F dừng lại, mọi hàm bị hoãn lại trong F được thực thi bình thường và sau đó F quay trở lại trình gọi của nó. Quá trình tiếp tục lên ngăn xếp cho đến khi tất cả các hàm trong goroutine hiện tại đã trả về. tại thời điểm đó chương trình bị treo. "
Cá nhân tôi muốn sử dụng phạm vi trên một kênh trong một quy trình:
https://play.golang.org/p/qt48vvDu8cd
Dave đã viết một bài rất hay về điều này: http://dave.cheney.net/2013/04/30/curious-channels .