【问题标题】:Goroutine implementation doubtsGoroutine 实现的疑惑
【发布时间】:2016-05-30 05:37:28
【问题描述】:

我需要一些帮助来理解如何在这个问题中使用 goroutine。我将只发布一些 sn-ps 代码,但如果您想深入了解,可以查看 here

基本上,我有一个分发器函数,它接收被多次调用的请求切片,每次调用该函数时,它必须在其他函数中分发该请求以实际解决请求。以及我正在尝试创建一个通道并启动此功能以解决新 goroutine 上的请求,因此程序可以同时处理请求。

distribute 函数是如何调用的:

// Run trigger the system to start receiving requests
func Run() {

    // Since the programs starts here, let's make a channel to receive requests
    requestCh := make(chan []string)
    idCh := make(chan string)

    // If you want to play with us you need to register your Sender here
    go publisher.Sender(requestCh)
    go makeID(idCh)
    // Our request pool
    for request := range requestCh {

        // add ID
        request = append(request, <-idCh)

        // distribute
        distributor(request)
    }

    // PROBLEM
    for result := range resultCh {
        fmt.Println(result)
    }
}

分发函数本身:

// Distribute requests to respective channels.
// No waiting in line. Everybody gets its own goroutine!
func distributor(request []string) {

    switch request[0] {

    case "sum":
        arithCh := make(chan []string)
        go arithmetic.Exec(arithCh, resultCh)
        arithCh <- request
    case "sub":
        arithCh := make(chan []string)
        go arithmetic.Exec(arithCh, resultCh)
        arithCh <- request
    case "mult":
        arithCh := make(chan []string)
        go arithmetic.Exec(arithCh, resultCh)
        arithCh <- request
    case "div":
        arithCh := make(chan []string)
        go arithmetic.Exec(arithCh, resultCh)
        arithCh <- request
    case "fibonacci":
        fibCh := make(chan []string)
        go fibonacci.Exec(fibCh, resultCh)
        fibCh <- request
    case "reverse":
        revCh := make(chan []string)
        go reverse.Exec(revCh, resultCh)
        revCh <- request
    case "encode":
        encCh := make(chan []string)
        go encode.Exec(encCh, resultCh)
        encCh <- request
    }
}

还有 fibonacci.Exec 函数来说明我如何尝试计算斐波那契给定在 fibCh 上收到的请求并通过 resultCh 发送结果值。

func Exec(fibCh chan []string, result chan map[string]string) {

    fib := parse(<-fibCh)
    nthFibonacci(fib)

    result <- fib
}

到目前为止,在 Run 函数中,当我覆盖 resultCh 时,我得到了结果,但也出现了死锁。但为什么?另外,我想我应该使用 waitGroup 函数来等待 goroutines 完成,但我不确定如何实现它,因为我期待收到连续的请求流。对于理解我在这里做错了什么以及解决它的方法,我将不胜感激。

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

    我并没有深入研究您的应用程序的实现细节,但基本上在我看来,您可以使用workers 模式。

    使用workers 模式,多个 goroutine 可以从单个通道读取,在 CPU 内核之间分配一定量的工作,因此是 worker 的名称。在 Go 中,这种模式很容易实现 - 只需以通道作为参数启动多个 goroutine,然后将值发送到该通道 - 分发和多路复用将由 Go 运行时自动完成。

    这是工人模式的简单实现:

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func worker(tasksCh <-chan int, wg *sync.WaitGroup) {
        defer wg.Done()
        for {
            task, ok := <-tasksCh
            if !ok {
                return
            }
            d := time.Duration(task) * time.Millisecond
            time.Sleep(d)
            fmt.Println("processing task", task)
        }
    }
    
    func pool(wg *sync.WaitGroup, workers, tasks int) {
        tasksCh := make(chan int)
    
        for i := 0; i < workers; i++ {
            go worker(tasksCh, wg)
        }
    
        for i := 0; i < tasks; i++ {
            tasksCh <- i
        }
    
        close(tasksCh)
    }
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(36)
        go pool(&wg, 36, 50)
        wg.Wait()
    }
    

    另一个有用的资源是如何使用WaitGroup 等待所有 goroutine 完成执行才能继续(因此不会陷入死锁)是这篇不错的文章:

    http://nathanleclaire.com/blog/2014/02/15/how-to-wait-for-all-goroutines-to-finish-executing-before-continuing/

    还有一个非常基本的实现:

    Go playground

    如果您不想更改实现以使用 worker 模式,那么使用另一个通道来表示 goroutine 执行结束可能是一个好主意,因为当没有接收者接受发送的消息时会发生死锁通过无缓冲通道。

    done := make(chan bool)
    //.....
    done <- true //Tell the main function everything is done.
    

    因此,当您收到消息时,您可以通过将通道值设置为 true 来将执行标记为已完成。

    【讨论】:

    • 感谢您的回答,但是,尽管您建议在这里使用工作者模式(我应该从一开始就考虑的事情)。您是否认为可以对我的旧实现进行改进而不是重新开始?我这么说是因为在我使用斐波那契函数之前,一切似乎都正常。问题似乎是由我启动斐波那契的方式引起的 goroutine 泄漏。
    • 这里我需要做的就是向分发函数发出请求,同时启动斐波那契函数,一旦该例程有结果,将结果发送到 resultCh,以便我可以重新发送发给客户并在那里打印。
    • 如果您在分支 dev pastebin.com/BFxnFa61 上运行代码会出现错误,其他分支正在运行,但可能存在内存泄漏。
    • 尝试使用另一个通道来表示通道操作的执行。请参阅我的更新答案。
    猜你喜欢
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 2020-11-02
    • 2013-02-24
    • 2017-02-16
    • 2013-09-06
    相关资源
    最近更新 更多