在Go語言中,程序的并發(fā)操作是通過通道(channel)來實(shí)現(xiàn)的。通道是用來傳遞數(shù)據(jù)的一種特殊類型,它可以在goroutine之間進(jìn)行數(shù)據(jù)交換和通信。然而,如果在程序中使用單通道進(jìn)行工作,并在引入新通道時(shí)沒有正確處理,就有可能導(dǎo)致死鎖現(xiàn)象的發(fā)生。本文將由php小編小新為大家詳細(xì)解釋Go程序中單通道工作和死鎖問題,以及如何避免死鎖的發(fā)生。
問題內(nèi)容
我是Go通道的新手,我正在嘗試通過構(gòu)建模擬內(nèi)核并通過通道處理交互來學(xué)習(xí)Go通道。此示例程序的目標(biāo)是讓多個(gè)進(jìn)程 (2) 使用單通道同時(shí)向內(nèi)核發(fā)送內(nèi)存分配請(qǐng)求,其他進(jìn)程發(fā)送釋放內(nèi)存請(qǐng)求 使用單個(gè)但不同的通道到內(nèi)核。
+-------------+ +------------------+ | | -> Alloc. Mem. Ch. |-->| Kernel | | Process A | | Realse Mem. Ch. |< | | +------------------+ +-------------+
登錄后復(fù)制
如果我只有分配請(qǐng)求,程序就可以工作,一旦我引入釋放請(qǐng)求,程序就會(huì)陷入死鎖。
請(qǐng)注意,進(jìn)程在發(fā)送分配請(qǐng)求時(shí)也會(huì)創(chuàng)建一個(gè)回復(fù)隊(duì)列,但是,這在上圖中沒有顯示,因?yàn)樗皇菃栴}的一部分。
完整的程序如下:
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) }
登錄后復(fù)制
異常情況如下:
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
登錄后復(fù)制
任何幫助將不勝感激。
干杯,
DD。
解決方法
作為英國人評(píng)論,您有一個(gè)緩沖通道已達(dá)到其容量,但沒有任何內(nèi)容可供讀取。
根據(jù)語言之旅 (1 2),發(fā)送和接收塊,直到另一方準(zhǔn)備好。雖然緩沖通道在這里提供了一些寬容,但一旦緩沖區(qū)已滿,行為是相同的。
可以通過添加 k.rlsMemCh
的使用者來解決此問題。如果您沒有為此計(jì)劃任何操作,請(qǐng)刪除該通道或暫時(shí)使用邏輯將其耗盡。
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
}
登錄后復(fù)制
排水可能如下所示:
func (k *Kernel) RlsMem() { for { <-k.GetRlsMemCh() } }
登錄后復(fù)制