In der Go-Sprache werden gleichzeitige Operationen des Programms über Kanäle implementiert. Ein Kanal ist ein spezieller Typ zur Datenübertragung. Er ermöglicht den Datenaustausch und die Kommunikation zwischen Goroutinen. Wenn Sie jedoch in Ihrem Programm mit einem einzelnen Kanal arbeiten und diesen beim Einführen eines neuen Kanals nicht richtig handhaben, kann es zu einem Deadlock kommen. In diesem Artikel erklärt der PHP-Editor Xiaoxin ausführlich die Single-Channel-Arbeit und Deadlock-Probleme in Go-Programmen und wie man Deadlocks vermeidet.
Ich bin neu bei Go-Kanälen und versuche, Go-Kanäle zu erlernen, indem ich einen Scheinkern baue und Interaktionen über Kanäle handhabe. Das Ziel dieses Beispielprogramms besteht darin, dass mehrere Prozesse (2) gleichzeitig Speicherzuweisungsanfragen über einen Kanal an den Kernel senden und andere Prozesse Speicherfreigabeanfragen über einzelne, aber unterschiedliche Kanäle an den Kernel senden .
+-------------+ +------------------+ | | -> Alloc. Mem. Ch. |<--\ | | +-----------------+ ---/ +------------------+ >-->| Kernel | | Process A |<-- +------------------+ -/ | | +-----------------+ \--> | Realse Mem. Ch. |< | | +------------------+ +-------------+
Wenn ich nur Zuteilungsanfragen habe, funktioniert das Programm, sobald ich Freigabeanfragen eingebe, gerät das Programm in einen Deadlock.
Beachten Sie, dass der Prozess beim Senden einer Zuteilungsanfrage auch eine Antwortwarteschlange erstellt. Dies wird jedoch im obigen Bild nicht angezeigt, da es nicht Teil des Problems ist.
Der vollständige Ablauf ist wie folgt:
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) }
Ausnahmen sind wie folgt:
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
Jede Hilfe wäre sehr dankbar.
Cheers,
DD.
Als britischer Kommentator haben Sie einen Pufferkanal, der seine Kapazität erreicht hat, aber nichts zu lesen hat.
Entsprechend der Sprachtour (1 2) Blöcke senden und empfangen, bis die andere Partei bereit ist. Obwohl gepufferte Kanäle hier eine gewisse Toleranz bieten, ist das Verhalten dasselbe, sobald der Puffer voll ist.
Dieses Problem kann gelöst werden, indem ein Benutzer von k.rlsMemCh
hinzugefügt wird. Wenn Sie hierfür nichts geplant haben, löschen Sie den Kanal oder nutzen Sie die Logik, um ihn vorübergehend zu entleeren.
<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>
Entwässerung könnte so aussehen:
func (k *Kernel) RlsMem() { for { <-k.GetRlsMemCh() } }
Das obige ist der detaillierte Inhalt vonDas Go-Programm arbeitet mit einem einzelnen Kanal und gerät in einen Deadlock, wenn ein neuer Kanal eingeführt wird. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!