在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中文网其他相关文章!