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

王林
Freigeben: 2024-02-09 23:20:10
nach vorne
1128 Leute haben es durchsucht

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. |<      |             |
                           +------------------+       +-------------+
Nach dem Login kopieren

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)
}
Nach dem Login kopieren

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
Nach dem Login kopieren

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>
Nach dem Login kopieren

Entwässerung könnte so aussehen:

func (k *Kernel) RlsMem() {
        for {
                <-k.GetRlsMemCh()
        }
}
Nach dem Login kopieren

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!

Verwandte Etiketten:
Quelle:stackoverflow.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!