Rumah >pembangunan bahagian belakang >Golang >Bagaimanakah Operasi Saluran Berantai dalam Penyata `select` Go Boleh Membawa kepada Kebuntuan dan Kehilangan Data?
Merantai Operasi Saluran dalam Kes Pilihan Tunggal: Menganalisis Kemungkinan Perangkap
Dalam Go, pernyataan pilih menyediakan cara yang mudah untuk memultiplekskan berbilang saluran. Pertimbangkan senario berikut: kami mempunyai dua saluran, A dan B, menghantar mesej pada selang masa yang berbeza. Kami ingin mencipta saluran peminat yang menerima mesej daripada A dan B.
Kod di bawah menunjukkan perkara ini:
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case t := <-input1: ch <- t case t := <-input2: ch <- t } } }() return ch }
Apabila kami menjalankan kod ini, kami menjangkakan untuk menerima mesej daripada kedua-dua saluran secara bersilang. Walau bagaimanapun, jika kami mengubah suai penyataan kes pilih seperti berikut, kami menghadapi gelagat yang tidak dijangka:
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case ch <- <-input1: case ch <- <-input2: } } }() return ch }
Dalam kes ini, kami menerima beberapa mesej dengan betul, tetapi kemudian kami mengalami penurunan nilai dan akhirnya menemui jalan buntu. Sebab bagi tingkah laku ini terletak pada kerja asas pilih.
Dalam pilih, hanya satu operasi baca atau tulis saluran tidak menyekat. Semua operasi lain berkelakuan normal. Dalam kod yang diubah suai, kedua-dua kes mengandungi penulisan saluran, yang tidak disekat. Ini membawa kepada situasi di mana mesej daripada saluran input beratur, tetapi saluran kipas masuk hanya boleh menggunakan satu demi satu.
Akibatnya, mesej boleh digugurkan dan kebuntuan berlaku apabila kipas masuk saluran tidak mempunyai penulis dan pembaca sedang menunggu lebih banyak nilai.
Untuk mengelakkan isu ini, adalah penting untuk memahami bahawa dalam pernyataan pilihan, hanya satu operasi harus tidak menyekat. Jika anda perlu melakukan operasi berbilang saluran dalam satu kes pilihan, pertimbangkan untuk menggunakan fungsi pembantu pilih yang tidak menyekat seperti ini:
func nonBlockingSelect(cases []reflect.SelectCase) (chosen int, recv interface{}, ok bool) { for i, c := range cases { if c.Dir == reflect.SelectSend && c.Chan == nil { continue } v, ok := reflect.Select(cases) return v.Index, v.Elem().Interface(), ok } return -1, nil, false }
Kemudian, fungsi kipas masuk yang diubah suai boleh ditulis semula sebagai:
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case c1 := <-input1: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c1)}, }) case c2 := <-input2: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c2)}, }) } } }() return ch }
Menggunakan pembantu pilih yang tidak menyekat memastikan bahawa hanya satu operasi saluran tidak menyekat, menghalang isu penurunan nilai dan kebuntuan.
Atas ialah kandungan terperinci Bagaimanakah Operasi Saluran Berantai dalam Penyata `select` Go Boleh Membawa kepada Kebuntuan dan Kehilangan Data?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!