在Go語言中,程式的並發操作是透過通道(channel)來實現的。通道是用來傳遞資料的一種特殊類型,它可以在goroutine之間進行資料交換和通訊。然而,如果在程式中使用單通道進行工作,並在引入新通道時沒有正確處理,就有可能導致死鎖現象的發生。本文將由php小編小新為大家詳細解釋Go程式中單通道工作和死鎖問題,以及如何避免死鎖的發生。
我是Go通道的新手,我正在嘗試透過建立類比核心並透過通道處理互動來學習Go通道。此範例程式的目標是讓多個進程(2) 使用單通道同時向核心發送記憶體分配請求,其他行程發送釋放記憶體請求 使用單一但不同的通道到核心。
+-------------+ +------------------+ | | -> Alloc. Mem. Ch. |<--\ | | +-----------------+ ---/ +------------------+ >-->| Kernel | | Process A |<-- +------------------+ -/ | | +-----------------+ \--> | Realse Mem. Ch. |< | | +------------------+ +-------------+
如果我只有分配請求,程式就可以工作,一旦我引入釋放請求,程式就會陷入死鎖。
請注意,進程在發送分配請求時也會建立回覆佇列,但是,這在上圖中沒有顯示,因為它不是問題的一部分。
完整的程式如下:
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) }
異常情況如下:
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
任何幫助將不勝感激。
乾杯,
DD。
作為英國人評論,您有一個緩衝通道已達到其容量,但沒有任何內容可供讀取。
根據語言之旅 (1 2),發送和接收區塊,直到另一方準備好。雖然緩衝通道在這裡提供了一些寬容,但一旦緩衝區已滿,行為是相同的。
可以透過新增 k.rlsMemCh
的使用者來解決此問題。如果您沒有為此計劃任何操作,請刪除該通道或暫時使用邏輯將其耗盡。
<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>
排水可能如下所示:
func (k *Kernel) RlsMem() { for { <-k.GetRlsMemCh() } }
以上是Go 程式使用單通道工作,並在引入新通道時陷入死鎖的詳細內容。更多資訊請關注PHP中文網其他相關文章!