如何有效地将频道链接在一起?

时间:2018-05-03 18:39:07

标签: go concurrency

我正在尝试在golang中创建消息中心。消息通过map[uint32]chan []float64中持续存在的不同渠道。我在地图上做了无限循环,检查频道是否有消息。如果有,我将它写入公共客户端的写通道以及传入通道的ID。它工作正常,但使用所有CPU,其他进程受到限制。

UPD:地图中的项目由另一个函数动态添加和删除。

我想通过Docker限制这个应用程序的CPU,但也许有更优雅的路径?

我的代码:

    func (c *Client) accumHandler() {

    for !c.stop {
        c.channels.Range(func(key, value interface{}) bool {

            select {
            case message := <-value.(chan []float64):
                mess := map[uint32]interface{}{key.(uint32): message}

                select {
                case c.send <- mess:

                }

            default:

            }
            return !c.stop
        })
    }
}

2 个答案:

答案 0 :(得分:1)

如果我正确读取卡片,您似乎正在尝试将浮动数组传递到公共通道以及通道标识符。我假设你这样做是为了将多个频道传递给不同的发布者,但这样你只需要为你的消费者跟踪一个频道。

事实证明,您不需要循环通道以查看它何时输出值。您可以在goroutines中将通道链接在一起。因此,不需要繁忙的等待。这样的东西将适合你的目的(再次,如果我正确地读卡)。寻找围绕忙碌循环的所有大写评论。 Link to playground

var num_publishes = 3

func main() {
  num_publishers := 10
  single_consumer := make(chan []float64)

  for i:=0;i<num_publishers;i+=1 {
    c := make(chan []float64)

    // connect channel to your single consumer channel
    go func() { for { single_consumer <- <-c } }() // THIS IS PROBABLY WHAT YOU DIDN'T KNOW ABOUT

    // send the channel to the publisher
    go publisher(c, i*100)
  }

  // dumb consumer example
  for i:=0;i<num_publishers*num_publishes;i+=1 {
    fmt.Println(<-single_consumer)
  }
}

func publisher(c chan []float64, publisher_id int) {
  dummy := []float64{
    float64(publisher_id+1),
    float64(publisher_id+2),
    float64(publisher_id+3),
  }
  for i:=0;i<num_publishes;i+=1 {
    time.Sleep(time.Duration(rand.Intn(10000)) * time.Millisecond)
    c <- dummy
  }
}

答案 1 :(得分:0)

它占用了你所有的CPU,因为你不断地在字典中循环检查消息,所以即使没有消息来处理CPU,或者至少是一个线程或核心,它也正在运行。你需要在频道上阻止发送和接收!

我认为你这样做是因为你不知道会有多少频道,因此不能在所有输入频道上select。一个更好的模式是为当前存储在字典中的每个输入通道启动一个单独的goroutine。每个goroutine都应该有一个循环,在这个循环中它阻塞等待输入通道,并且在接收消息时阻塞发送到客户端的通道,由所有人共享。

这个问题还没有完成,所以无法给出确切的代码,但是您将会看到类似这样的goroutines:

type Message struct {
    id uint32,
    message []float64
}

func receiverGoroutine(id uint32, input chan []float64, output chan Message) {
    for {
        message := <- input
        output <- Message{id: id, message: message}
    }
}

func clientGoroutine(c *Client, input chan Message) {
    for {
        message := <- input
        // do stuff
    }
}

(你还需要添加一些&#34;完成&#34;频道)

在其他地方,您将使用以下代码启动它们:

clientChan := make(chan Message)
go clientGoroutine(client, clientChan)

for i:=0; i<max; i++ {
    go receiverGoroutine( (uint32)i, make(chan []float64, clientChan)
}

或者您可以启动客户端例程,然后根据需要添加其他例程而不是预先循环 - 取决于您的用例。