Jawaban lain, tergantung pada situasi Anda, adalah dengan menggunakan Konteks Go. Saya tidak punya waktu atau pengetahuan untuk membuat jawaban tentang ini. Saya hanya ingin menyebutkannya di sini sehingga orang yang mencari dan menemukan jawaban ini tidak memuaskan memiliki utas lain untuk ditarik (permainan kata-kata). Dalam kebanyakan kasus, Anda harus melakukan apa yang disarankan oleh jawaban yang diterima. Jawaban ini menyebutkan konteks: stackoverflow.com/a/47302930/167958
Omnifarious
Jawaban:
51
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 mainimport"sync"
func main(){var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func(){for{
foo, ok :=<- chif!ok {
println("done")
wg.Done()return}
println(foo)}}()
ch <-1
ch <-2
ch <-3
close(ch)
wg.Wait()}
Isi goroutine bagian dalam lebih idiomatis ditulis menggunakan deferto call wg.Done(), dan range chloop untuk mengulang semua nilai hingga saluran ditutup.
Alan Donovan
115
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:returndefault:// Do other stuff}}}()// Do stuff// Quit goroutine
quit <-true
Tidak cukup baik. Bagaimana jika goroutine terjebak dalam putaran tak berujung, karena bug?
Elazar Leibovich
232
Kemudian bug harus diperbaiki.
jimt
13
Elazar, Apa yang Anda sarankan adalah cara menghentikan suatu fungsi setelah Anda memanggilnya. Goroutine bukanlah utas. Ini mungkin berjalan di utas yang berbeda atau mungkin berjalan di utas yang sama dengan milik Anda. Saya tahu tidak ada bahasa yang mendukung apa yang menurut Anda harus didukung oleh Go.
Jeremy Wall
5
@jeremy Tidak tidak setuju untuk Go, tetapi Erlang memungkinkan Anda untuk menghentikan proses yang menjalankan fungsi perulangan.
MatthewToday
10
Melakukan multitasking itu kooperatif, bukan preemptive. Goroutine dalam satu loop tidak pernah memasuki penjadwal, sehingga tidak akan pernah bisa dimatikan.
Jeff Allen
34
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.
Dan Anda mungkin ingin melihat ke dalam paket encoding / gob, yang memungkinkan dua program Go dengan mudah bertukar struktur data melalui pipa.
Jeff Allen
Dalam kasus saya, saya memiliki goroutine yang akan diblokir pada panggilan sistem, dan saya perlu memberitahukannya untuk membatalkan panggilan sistem dan kemudian keluar. Jika saya diblokir pada pembacaan saluran, itu mungkin untuk melakukan apa yang Anda sarankan.
Omnifarious
Saya melihat masalah itu sebelumnya. Cara kami "menyelesaikannya" adalah dengan meningkatkan jumlah utas di awal aplikasi untuk mencocokkan jumlah goroutine yang mungkin + jumlah CPU
rouzier
20
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{return1}
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{}{}returndefault:
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 Procstruct{Tomb tomb.Tomb}
func (proc *Proc)Exec(){
defer proc.Tomb.Done()// Must call only oncefor{select{case<-proc.Tomb.Dying():returndefault:
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
Paket ini cukup menarik! Sudahkah Anda menguji untuk melihat apa yang tombdilakukan 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()...
Gwyneth Llewelyn
1
Hai Gwyneth, ya 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. "
Kevin Cantwell
7
Secara pribadi, saya ingin menggunakan range pada saluran di goroutine:
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):
sumber
defer
to callwg.Done()
, danrange 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.
sumber
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.
sumber
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 demo
context.WithCancel
Demo pertama, gunakan
channel
:Demo kedua, gunakan
context
:sumber
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:
Outputnya akan terlihat seperti:
sumber
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 deferredproc.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 .
sumber