Home >Backend Development >Golang >Go program works with single channel and gets into deadlock when new channel is introduced
In the Go language, concurrent operations of the program are implemented through channels. A channel is a special type used to transfer data. It allows data exchange and communication between goroutines. However, if you work with a single channel in your program and do not handle it correctly when introducing a new channel, deadlock may occur. In this article, PHP editor Xiaoxin will explain in detail the single-channel work and deadlock issues in Go programs, and how to avoid deadlocks.
I am new to Go channels and I am trying to learn Go channels by building a mock kernel and handling interactions through channels. The goal of this sample program is to have multiple processes (2) simultaneously send memory allocation requests to the kernel using single channel , and other processes to send release memory requests using Single but distinct channels to the kernel.
+-------------+ +------------------+ | | -> Alloc. Mem. Ch. |<--\ | | +-----------------+ ---/ +------------------+ >-->| Kernel | | Process A |<-- +------------------+ -/ | | +-----------------+ \--> | Realse Mem. Ch. |< | | +------------------+ +-------------+
If I only have allocation requests, the program works, once I introduce release requests, the program gets into a deadlock.
Please note that the process also creates a reply queue when sending an allocation request, however, this is not shown in the above image because it is not part of the problem.
The complete procedure is as follows:
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) }
Exceptions are as follows:
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
Any help would be greatly appreciated.
cheers,
DD.
As British commenter, you have a buffer channel that has reached its capacity but has nothing to read.
According to the language tour (1 2), blocks are sent and received until the other party is ready. Although buffered channels provide some tolerance here, once the buffer is full, the behavior is the same.
This problem can be solved by adding a user of k.rlsMemCh
. If you don't have anything planned for this, delete the channel or use logic to drain it temporarily.
<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>
Drainage may look like this:
func (k *Kernel) RlsMem() { for { <-k.GetRlsMemCh() } }
The above is the detailed content of Go program works with single channel and gets into deadlock when new channel is introduced. For more information, please follow other related articles on the PHP Chinese website!