更新未读的频道数据

时间:2017-11-04 04:57:13

标签: go

有没有办法更新已发送到具有更新数据的频道的未读数据?

我有一个goroutine(制作人),其频道为另一个goroutine(消费者)提供进度更新。在某些情况下,进度可以比消费者使用更新消息更快地更新。

这使我遇到问题:

  • 阻止向频道发送数据。这意味着如果消费者读取数据的速度很慢,那么进度更新goroutine会完全阻止 - 它不应该这样做。
  • 当频道已满时,请勿阻止发送和跳过进度更新。这意味着消费者总是从数据数据中读取旧的。

举个例子,我可能会有这样的事情:

Progress reporting goroutine: Posts "1%" to channel Progress reporting goroutine: Posts "2%" to channel Progress reporting goroutine: Posts "3%" to channel Progress consuming goroutine: Reads "1%", "2%" and "3%" from channel. "1% and "2%" are outdated information.

有没有办法更新未读的频道数据?或者有更好的方法来解决这个问题吗?

4 个答案:

答案 0 :(得分:2)

您可以将某些值存储在受RWMutex保护的全局变量中。它会保持进度。发电机更新它。消费者阅读和节目。

此外,您可以对长度为1的通道进行非阻塞写入:

var c = make(chan struct{}, 1)
select {
case c <- struct{}{}:
default:
}

这种方式发送者要么在通道中添加一个元素,要么在它已满时不执行任何操作。

Reader将这个空结构视为一个信号 - 它应该采用全局变量中的更新值。

另一种方式:可更新频道

var c = make(chan int, 1)
select {
case c <- value:  // channel was empty - ok

default: // channel if full - we have to delete a value from it with some precautions to not get locked in our own channel
    switch {
    case <- c: // read stale value and put a fresh one
         c <- value
    default: // consumer have read it - so skip and not get locked
     }
}

答案 1 :(得分:1)

package main

import "fmt"

func main()  {
    // channel buffer must be 1 in this case
    var ch = make(chan int, 1)

    // when channel was not consumed and you want to update value at channel
    produceToChannel(ch, 1)
    produceToChannel(ch, 2)
    fmt.Println(consumeFromChannel(ch)) // prints 2

    // when channel was already consumed and you are producing new value
    produceToChannel(ch, 3)
    consumeFromChannel(ch)
    produceToChannel(ch, 4)
    fmt.Println(consumeFromChannel(ch)) // prints 4
}

func produceToChannel(ch chan int, v int) {
    select {
    case <-ch:
    default:
    }
    ch <- v
}

func consumeFromChannel(ch chan int) int {
    return <- ch
}

答案 2 :(得分:0)

每次在向频道发送值之前清除频道。

换句话说,当您发送3%时,3%成为频道中的唯一值。

您可以使用缓冲区长度为1的频道,只需使用<-ch清除频道即可。

编辑:使用默认情况下的select清除频道<-ch,因此在前一个值已被读取的情况下不要阻止。

答案 3 :(得分:0)

存储所有入站对象版本的并发映射如何,0表示默认版本

import "sync"
var Versions sync.Map = sync.Map{}
type Data struct {
    Payload interface{}
    Version int
    ID int
}

func produce(c chan Data) {
    for {
        data := produceData()
        if hasNewVersion(data) {
            Versions.Store(data.ID, data.Version)
        }
        c <- data 
    }
}

func consume(c chan Data) {
    for {
        select {
        case val:= <- c:
            if ver, ok := Versions.Load(val.ID); ok {
                if ver.(int) == val.Version {
                    // process
                }
            }
        }
    }
}