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

王林
Lepaskan: 2024-02-09 23:20:10
ke hadapan
1128 orang telah melayarinya

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. |<      |             |
                           +------------------+       +-------------+
Salin selepas log masuk

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)
}
Salin selepas log masuk

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
Salin selepas log masuk

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>
Salin selepas log masuk

Parit mungkin kelihatan seperti ini:

func (k *Kernel) RlsMem() {
        for {
                <-k.GetRlsMemCh()
        }
}
Salin selepas log masuk

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!

Label berkaitan:
sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!