我需要一些帮助,以了解如何在此问题中使用goroutine。我将只发布一些代码片段,但是如果您想深入了解,可以查看here

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

分配函数的调用方式:

// 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函数来等待goroutine完成,但是我不确定如何实现该功能,因为我希望收到连续的请求流。在理解我在这里做错的事情以及解决问题的方法方面,我将不胜感激。

最佳答案

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

使用workers模式,多个goroutine可以从单个 channel 读取,从而在CPU内核之间分配大量工作,因此分配了 worker 名称。在Go中,此模式易于实现-只需以 channel 为参数启动多个goroutine,然后将值发送至该 channel -Go运行时将自动进行分配和多路复用。

这是worker模式的简单实现:

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模式,则最好使用另一个 channel 来表示goroutine执行的结束,因为当没有接收者通过未缓冲的 channel 来接受发送的消息时,就会发生死锁。
done := make(chan bool)
//.....
done <- true //Tell the main function everything is done.

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

关于go - Goroutine实现疑虑,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35474949/

10-10 13:36
查看更多