Heim  >  Artikel  >  Backend-Entwicklung  >  Das Go-Programm arbeitet mit einem einzelnen Kanal und gerät in einen Deadlock, wenn ein neuer Kanal eingeführt wird

Das Go-Programm arbeitet mit einem einzelnen Kanal und gerät in einen Deadlock, wenn ein neuer Kanal eingeführt wird

王林
王林nach vorne
2024-02-09 23:20:101113Durchsuche

Go 程序使用单通道工作,并在引入新通道时陷入死锁

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.

Frageninhalt

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.

Workaround

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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen