首页  >  文章  >  后端开发  >  Go 程序使用单通道工作,并在引入新通道时陷入死锁

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

王林
王林转载
2024-02-09 23:20:101113浏览

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

在Go语言中,程序的并发操作是通过通道(channel)来实现的。通道是用来传递数据的一种特殊类型,它可以在goroutine之间进行数据交换和通信。然而,如果在程序中使用单通道进行工作,并在引入新通道时没有正确处理,就有可能导致死锁现象的发生。本文将由php小编小新为大家详细解释Go程序中单通道工作和死锁问题,以及如何避免死锁的发生。

问题内容

我是Go通道的新手,我正在尝试通过构建模拟内核并通过通道处理交互来学习Go通道。此示例程序的目标是让多个进程 (2) 使用单通道同时向内核发送内存分配请求,其他进程发送释放内存请求 使用单个但不同的通道到内核。

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

如果我只有分配请求,程序就可以工作,一旦我引入释放请求,程序就会陷入死锁。

请注意,进程在发送分配请求时也会创建一个回复队列,但是,这在上图中没有显示,因为它不是问题的一部分。

完整的程序如下:

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

异常情况如下:

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

任何帮助将不胜感激。

干杯,

DD。

解决方法

作为英国人评论,您有一个缓冲通道已达到其容量,但没有任何内容可供读取。

根据语言之旅 (1 2),发送和接收块,直到另一方准备好。虽然缓冲通道在这里提供了一些宽容,但一旦缓冲区已满,行为是相同的。

可以通过添加 k.rlsMemCh 的使用者来解决此问题。如果您没有为此计划任何操作,请删除该通道或暂时使用逻辑将其耗尽。

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

排水可能如下所示:

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

以上是Go 程序使用单通道工作,并在引入新通道时陷入死锁的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文转载于:stackoverflow.com。如有侵权,请联系admin@php.cn删除