untuk memulai loop tanpa akhir dalam mengeksekusi dua goroutine, saya dapat menggunakan kode di bawah ini:
setelah menerima pesan, itu akan memulai goroutine baru dan berlanjut selamanya.
c1 := make(chan string)
c2 := make(chan string)
go DoStuff(c1, 5)
go DoStuff(c2, 2)
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Sekarang saya ingin memiliki perilaku yang sama untuk N goroutine, tetapi bagaimana pernyataan select akan terlihat dalam kasus itu?
Ini adalah bit kode yang saya mulai, tetapi saya bingung bagaimana membuat kode pernyataan pilih
numChans := 2
//I keep the channels in this slice, and want to "loop" over them in the select statemnt
var chans = [] chan string{}
for i:=0;i<numChans;i++{
tmp := make(chan string);
chans = append(chans, tmp);
go DoStuff(tmp, i + 1)
//How shall the select statment be coded for this case?
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Jawaban:
Anda dapat melakukan ini menggunakan
Select
fungsi dari paket refleksi :Anda meneruskan larik
SelectCase
struct yang mengidentifikasi saluran untuk dipilih, arah operasi, dan nilai untuk dikirim dalam kasus operasi pengiriman.Jadi Anda bisa melakukan sesuatu seperti ini:
Anda dapat bereksperimen dengan contoh yang lebih lengkap di sini: http://play.golang.org/p/8zwvSk4kjx
sumber
Anda dapat melakukannya dengan menggabungkan setiap saluran dalam goroutine yang "meneruskan" pesan ke saluran "agregat" bersama. Sebagai contoh:
Jika Anda perlu mengetahui dari saluran mana pesan berasal, Anda dapat menggabungkannya dalam struct dengan informasi tambahan sebelum meneruskannya ke saluran agregat.
Dalam pengujian (terbatas) saya, metode ini bekerja sangat baik dengan menggunakan paket refleks:
Kode benchmark di sini
sumber
b.N
dalam patokan. Jika tidak, hasil (yang dibagi denganb.N
, 1 dan 2000000000 dalam output Anda) tidak akan berarti sama sekali.reflect.Select
pendekatannya) adalah goroutine melakukan buffer penggabungan minimal satu nilai pada setiap saluran yang akan digabungkan. Biasanya itu tidak akan menjadi masalah tetapi dalam beberapa aplikasi tertentu yang mungkin menjadi pemecah masalah :(.Untuk memperluas beberapa komentar pada jawaban sebelumnya dan untuk memberikan perbandingan yang lebih jelas, berikut adalah contoh dari kedua pendekatan yang disajikan sejauh ini dengan masukan yang sama, sepotong saluran untuk dibaca dan fungsi untuk memanggil setiap nilai yang juga perlu diketahui yang mana saluran asal nilai.
Ada tiga perbedaan utama antara pendekatan tersebut:
Kompleksitas. Meskipun ini mungkin sebagian dari preferensi pembaca, saya menemukan pendekatan saluran lebih idiomatik, langsung, dan mudah dibaca.
Performa. Pada sistem Xeon amd64 saya, saluran keluar goroutines + melakukan solusi refleksi sekitar dua kali lipat (secara umum refleksi di Go seringkali lebih lambat dan hanya boleh digunakan ketika benar-benar diperlukan). Tentu saja, jika ada penundaan yang signifikan baik dalam fungsi pemrosesan hasil atau dalam penulisan nilai ke saluran input, perbedaan kinerja ini dapat dengan mudah menjadi tidak signifikan.
Semantik pemblokiran / buffering. Pentingnya ini tergantung pada kasus penggunaan. Seringkali itu tidak masalah atau sedikit buffering ekstra dalam solusi penggabungan goroutine mungkin berguna untuk throughput. Namun, jika diinginkan untuk memiliki semantik yang hanya satu penulis yang tidak diblokir dan nilainya ditangani sepenuhnya sebelum penulis lain dibuka blokirnya, maka itu hanya dapat dicapai dengan solusi reflektif.
Catatan, kedua pendekatan dapat disederhanakan jika "id" dari saluran pengirim tidak diperlukan atau jika saluran sumber tidak akan pernah ditutup.
Saluran penggabungan Goroutine:
Pilihan refleksi:
[Kode lengkap di taman bermain Go .]
sumber
select
ataureflect.Select
tidak. Goroutine akan terus berputar sampai mereka mengkonsumsi semuanya dari saluran, jadi tidak ada cara yang jelas untukProcess1
keluar lebih awal. Ada juga potensi masalah jika Anda memiliki banyak pembaca, karena goroutine menyangga satu item dari setiap saluran, yang tidak akan terjadi denganselect
.select
dalam satufor
loop, bukanfor range
loop sederhana yang saat ini digunakan. Process2 perlu memasukkan kasus lain ke dalamcases
dan menangani khusus nilai itui
.Mengapa pendekatan ini tidak berhasil dengan asumsi bahwa seseorang mengirim peristiwa?
sumber
select
beberapa saluran (tanpadefault
klausa) adalah bahwa ia menunggu secara efisien hingga setidaknya satu saluran siap tanpa berputar.Opsi yang mungkin lebih sederhana:
Alih-alih memiliki array saluran, mengapa tidak meneruskan hanya satu saluran sebagai parameter ke fungsi yang dijalankan pada goroutine terpisah, dan kemudian mendengarkan saluran tersebut dalam goroutine konsumen?
Ini memungkinkan Anda untuk memilih hanya pada satu saluran di pendengar Anda, membuat pemilihan sederhana, dan menghindari pembuatan goroutine baru untuk mengumpulkan pesan dari beberapa saluran?
sumber