Dalam bahasa Go, operasi serentak program dilaksanakan melalui saluran. Saluran ialah jenis khas yang digunakan untuk memindahkan data Ia membolehkan pertukaran data dan komunikasi antara goroutine. Walau bagaimanapun, jika anda bekerja dengan satu saluran dalam program anda dan tidak mengendalikannya dengan betul semasa memperkenalkan saluran baharu, kebuntuan mungkin berlaku. Dalam artikel ini, editor PHP Xiaoxin akan menerangkan secara terperinci tentang kerja saluran tunggal dan isu kebuntuan dalam program Go, dan cara mengelakkan kebuntuan.
Saya baru menggunakan saluran Go dan saya cuba mempelajari saluran Go dengan membina inti tiruan dan mengendalikan interaksi melalui saluran. Matlamat program contoh ini adalah untuk mempunyai berbilang proses (2) secara serentak menghantar permintaan peruntukan memori ke kernel menggunakan saluran tunggal, dan proses lain untuk menghantar melepaskan permintaan memori ke kernel menggunakan saluran tunggal tetapi berbeza .
+-------------+ +------------------+ | | -> Alloc. Mem. Ch. |<--\ | | +-----------------+ ---/ +------------------+ >-->| Kernel | | Process A |<-- +------------------+ -/ | | +-----------------+ \--> | Realse Mem. Ch. |< | | +------------------+ +-------------+
Jika saya hanya mempunyai permintaan peruntukan, program berfungsi, sebaik sahaja saya memperkenalkan permintaan keluaran, program akan menemui jalan buntu.
Perhatikan bahawa proses itu juga mencipta baris gilir balasan apabila menghantar permintaan peruntukan, namun, ini tidak ditunjukkan dalam imej di atas kerana ia bukan sebahagian daripada masalah.
Prosedur lengkap adalah seperti berikut:
package main import ( "fmt" // "log" "time" ) const ( _ float64 = iota LowPrio MedPrio HghPrio ) // Kernel type to communicate between processes and memory resources type Kernel struct { reqMemCh chan chan int rlsMemCh chan int } func (k *Kernel) Init() { k.reqMemCh = make(chan chan int, 2) k.rlsMemCh = make(chan int, 2) go k.AllocMem() go k.RlsMem() } // Fetch memory on process request func (k *Kernel) GetReqMemCh() chan chan int { return k.reqMemCh } func (k *Kernel) GetRlsMemCh() chan int { return k.rlsMemCh } func (k *Kernel) AllocMem() { // loop over the items (process reply channels) received over // the request channel for pCh := range k.GetReqMemCh() { // for now think 0 is the available index // send this as a reply to the exclusive process reply channel pCh <- 0 close(pCh) } } // Release memory func (k *Kernel) RlsMem() { // we do not have to anything here } // Process type which requests memory type Proc struct { ind int prio float64 exeT time.Time count int memInd int rqMemCh chan chan int rlMemCh chan int } func (p *Proc) Init( ind int, prio float64, rqMemCh chan chan int, rlMemCh chan int, ) { p.ind = ind p.prio = prio p.memInd = -1 p.rqMemCh = rqMemCh p.rlMemCh = rlMemCh } func (p *Proc) GetReqMemCh() chan chan int { return p.rqMemCh } func (p *Proc) GetRlsMemCh() chan int { return p.rlMemCh } func (p *Proc) ReqMem() { // create the reply channel exclusive to the process // this channel will return the allocated memeory id/address rpCh := make(chan int) // send the reply channel through the request channel // to get back the allocation memory id p.GetReqMemCh() <- rpCh // Below line is blocking ... for mi := range rpCh { p.memInd = mi } } func (p Proc) RlsMem() { p.GetRlsMemCh() <- 0 } func (p Proc) String() string { return fmt.Sprintf( "Proc(%d): Memory(%d), Count(%d)", p.ind+1, p.memInd+1, p.count, ) } func main() { k := &Kernel{} k.Init() p := &Proc{} for i := 0; i < 3; i++ { p.Init(i, LowPrio, k.GetReqMemCh(), k.GetRlsMemCh()) p.ReqMem() p.RlsMem() } time.Sleep(time.Second) }
Pengecualian adalah seperti berikut:
fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.Proc.RlsMem(...) main.go:100 main.main() main.go:119 +0xc5 goroutine 6 [chan receive]: main.(*Kernel).AllocMem(0x0?) main.go:41 +0x5e created by main.(*Kernel).Init in goroutine 1 main.go:25 +0xc5 exit status 2
Sebarang bantuan amat kami hargai.
Sekian,
DD.
Sebagai Pengulas Inggeris, anda mempunyai saluran penimbal yang telah mencapai kapasitinya tetapi tiada apa-apa untuk dibaca.
Mengikut lawatan bahasa (1 2), hantar dan terima blok sehingga pihak lain bersedia. Walaupun saluran penimbal memberikan sedikit toleransi di sini, setelah penimbal penuh, gelagatnya adalah sama.
Masalah ini boleh diselesaikan dengan menambah pengguna k.rlsMemCh
. Jika anda tidak mempunyai apa-apa rancangan untuk ini, padamkan saluran atau gunakan logik untuk mengalirkannya buat sementara waktu.
<code>func (k *Kernel) Init() { k.reqMemCh = make(chan chan int, 2) k.rlsMemCh = make(chan int, 2) go k.AllocMem() go k.RlsMem() } func (k *Kernel) AllocMem() { for pCh := range k.GetReqMemCh() { pCh <- 0 close(pCh) } } func (k *Kernel) RlsMem() { // TODO: Add a for-select or for-range over k.rlsMemCh here } </code>
Parit mungkin kelihatan seperti ini:
func (k *Kernel) RlsMem() { for { <-k.GetRlsMemCh() } }
Atas ialah kandungan terperinci Program Go berfungsi dengan saluran tunggal dan menemui jalan buntu apabila saluran baharu diperkenalkan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!