当前位置: 首页 > 面试题库 >

在时间间隔和通道长度之间进行选择

东郭臻
2023-03-14
问题内容

我在这里找到执行跟踪任务的最惯用的方法。

任务

将数据从通道写入文件。

问题

我有一个频道 ch := make(chan int, 100)

我需要从通道读取并将从通道读取的值写入文件。我的问题基本上是鉴于

  1. 如果通道ch已满,请立即写入值
  2. 如果通道ch未满,则每5s写一次。

因此,从本质上讲,至少需要每5s将数据写入文件一次(假设至少每5s将数据填充到通道中一次)

请告诉我用最好的方式selectforrange做到我上面的任务吗?

谢谢!


问题答案:

没有“ 通道缓冲区已满” 这样的“事件” ,因此您无法检测到[*]。这意味着您不能仅使用1个通道就用语言原语惯用地解决您的问题。

[*]并非完全如此:你可以检测一个信道的缓冲区已满通过使用selectdefault当情况下 发送
的信道上,但是这需要从发送者,并重复尝试发送逻辑。

我将使用从中接收值发送的另一个通道,然后“重定向”,将值存储在另一个通道中,该通道的缓冲区为100,如您所提到的。在每次重定向时,您都可以检查内部通道的缓冲区是否已满,如果已满,请立即进行写操作。如果不是,请继续使用select语句监视“传入”通道和计时器通道,如果计时器启动,则执行“常规”写入。

您可以len(chInternal)用来检查chInternal通道中有多少个元素,并cap(chInternal)检查其容量。请注意,这是“安全的”,因为我们是唯一处理该chInternal通道的goroutine
。如果将有多个goroutine,则len(chInternal)在我们将其用于某些事物时(例如,对其进行比较),返回的值可能已过时。

在此解决方案中chInternal(顾名思义)仅供内部使用。其他人只能在上发送值ch。请注意,ch可能是缓冲通道,也可能不是缓冲通道,在两种情况下解决方案均有效。但是,如果您还提供一些缓冲,则可以提高效率ch(这样,发件人被阻止的机会就会减少)。

var (
    chInternal = make(chan int, 100)
    ch         = make(chan int) // You may (should) make this a buffered channel too
)

func main() {
    delay := time.Second * 5
    timer := time.NewTimer(delay)
    for {
        select {
        case v := <-ch:
            chInternal <- v
            if len(chInternal) == cap(chInternal) {
                doWrite() // Buffer is full, we need to write immediately
                timer.Reset(delay)
            }
        case <-timer.C:
            doWrite() // "Regular" write: 5 seconds have passed since last write
            timer.Reset(delay)
        }
    }
}

如果立即写入(由于“缓冲区已满”的情况),此解决方案将在此之后5秒钟对下一次“常规”写入进行计时。如果您不希望这样做,并且希望5秒钟常规写入独立于立即写入,则只需在立即写入后不重置计时器即可。

的实现doWrite()可能如下:

var f *os.File // Make sure to open file for writing

func doWrite() {
    for {
        select {
        case v := <-chInternal:
            fmt.Fprintf(f, "%d ", v) // Write v to the file
        default: // Stop when no more values in chInternal
            return
        }
    }
}

我们不能使用for ... range该方法,因为它仅在关闭通道时返回,而我们的chInternal通道没有关闭。因此,我们使用select带有的default情况,以便当的缓冲区中没有更多值时chInternal,我们返回。

改进之处

使用切片而不是第二通道

由于chInternal通道仅由我们使用,并且仅在单个goroutine上使用,因此我们也可以选择使用单个[]int片而不是通道(读/写片比通道快得多)。

仅显示不同/已更改的部分,看起来可能像这样:

var (
    buf = make([]int, 0, 100)
)

func main() {
    // ...

    for {
        select {
        case v := <-ch:
            buf = append(buf, v)
            if len(buf) == cap(buf) {
            // ...
    }
}

func doWrite() {
    for _, v := range buf {
        fmt.Fprintf(f, "%d ", v) // Write v to the file
    }
    buf = buf[:0] // "Clear" the buffer
}

有多个goroutines

如果我们坚持离开chInternal一个通道,则doWrite()可以在另一个goroutine上调用该函数以不阻塞另一个通道,例如go doWrite()。由于要从通道(chInternal)读取要写入的数据,因此不需要进一步的同步。



 类似资料:
  • 桌面视频 我想选择从给定时间间隔6个月的行。新的还是旧的 因此,如果查询时间为例如1446076800,则将选择比该时间早6个月或新6个月的所有行。 采用UNIX TIMESTAMP格式。 因此,如果一个视频是在2015年10月上传的,所有在2015年4月上传的视频和更新的视频都将被选中。从2015年10月到2016年4月的所有视频也将被选中。

  • 问题内容: 我正在使用XmlHttpRequests将图像上传到服务器,并且想向用户显示这些上传的进度。 不幸的是,对我的onprogress-event处理程序的调用之间的间隔太大。对于500k图像,通常仅一次或两次调用onprogress。 这是我的代码: 可以更改此行为,还是在浏览器实现中将其硬编码? 问题答案: W3在其XMLHttpRequest Level 2 文档中提出了以下准则。显

  • 我有一组不重叠的,不相邻的区间,例如[{10,15},{30,35},{20,25}]。它们没有排序,但如果需要,我可以对它们进行排序。 现在,我得到了一些新的区间,例如{5,32},并希望生成一组新的区间来描述差异:这个新区间所覆盖的范围不在该集合中。在这个例子中,答案是:[{5,9},{16,19},{26,29}]。 计算这个的快速算法是什么?请注意,集合中通常有1个,有时有2个,很少有3个

  • 我有一个包含一系列事件及其时间戳的数据库。 我在这里读到这是可以在SQLite中实现的,我想知道是否也可以在presto中实现。我查看了文档,但找不到一个类似的函数来执行SQLite中的操作。

  • # interval(date) Alias for interval.floor(date). For example, d3.time.day(new Date()) returns midnight (12:00 AM) on the current day, in local time. # interval.floor(date) Rounds down the specified da

  • 试图在空手道框架和应用编程接口自动化的Rest保证之间进行选择,包括Rest应用编程接口和图形QL。我们还可以增强框架来执行用户界面自动化 我经历了寻找的一般过程: 空手道专为GraphQL设计:https://stackoverflow.com/a/47469363/143475 空手道和“放心”的比较:https://github.com/intuit/karate#comparison-放心