Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Program Go berfungsi dengan saluran tunggal dan menemui jalan buntu apabila saluran baharu diperkenalkan

Program Go berfungsi dengan saluran tunggal dan menemui jalan buntu apabila saluran baharu diperkenalkan

王林
王林ke hadapan
2024-02-09 23:20:101113semak imbas

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

Dalam bahasa Go, operasi serentak program dilaksanakan melalui saluran. Saluran ialah jenis khas yang digunakan untuk memindahkan data Ia membolehkan pertukaran data dan komunikasi antara goroutine. Walau bagaimanapun, jika anda bekerja dengan satu saluran dalam program anda dan tidak mengendalikannya dengan betul semasa memperkenalkan saluran baharu, kebuntuan mungkin berlaku. Dalam artikel ini, editor PHP Xiaoxin akan menerangkan secara terperinci tentang kerja saluran tunggal dan isu kebuntuan dalam program Go, dan cara mengelakkan kebuntuan.

Kandungan soalan

Saya baru menggunakan saluran Go dan saya cuba mempelajari saluran Go dengan membina inti tiruan dan mengendalikan interaksi melalui saluran. Matlamat program contoh ini adalah untuk mempunyai berbilang proses (2) secara serentak menghantar permintaan peruntukan memori ke kernel menggunakan saluran tunggal, dan proses lain untuk menghantar melepaskan permintaan memori ke kernel menggunakan saluran tunggal tetapi berbeza .

+-------------+
                           +------------------+       |             |
                          -> Alloc. Mem. Ch.  |<--\   |             |
+-----------------+   ---/ +------------------+   >-->|   Kernel    |
|   Process A     |<--     +------------------+ -/    |             |
+-----------------+   \--> |  Realse Mem. Ch. |<      |             |
                           +------------------+       +-------------+

Jika saya hanya mempunyai permintaan peruntukan, program berfungsi, sebaik sahaja saya memperkenalkan permintaan keluaran, program akan menemui jalan buntu.

Perhatikan bahawa proses itu juga mencipta baris gilir balasan apabila menghantar permintaan peruntukan, namun, ini tidak ditunjukkan dalam imej di atas kerana ia bukan sebahagian daripada masalah.

Prosedur lengkap adalah seperti berikut:

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)
}

Pengecualian adalah seperti berikut:

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

Sebarang bantuan amat kami hargai.

Sekian,

DD.

Penyelesaian

Sebagai Pengulas Inggeris, anda mempunyai saluran penimbal yang telah mencapai kapasitinya tetapi tiada apa-apa untuk dibaca.

Mengikut lawatan bahasa (1 2), hantar dan terima blok sehingga pihak lain bersedia. Walaupun saluran penimbal memberikan sedikit toleransi di sini, setelah penimbal penuh, gelagatnya adalah sama.

Masalah ini boleh diselesaikan dengan menambah pengguna k.rlsMemCh. Jika anda tidak mempunyai apa-apa rancangan untuk ini, padamkan saluran atau gunakan logik untuk mengalirkannya buat sementara waktu.

<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>

Parit mungkin kelihatan seperti ini:

func (k *Kernel) RlsMem() {
        for {
                <-k.GetRlsMemCh()
        }
}

Atas ialah kandungan terperinci Program Go berfungsi dengan saluran tunggal dan menemui jalan buntu apabila saluran baharu diperkenalkan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam