如何同步常量写入和周期性读取和更新

定义问题:


我们有这个物联网设备,每个设备都会向我们发送有关汽车位置的日志。我们要计算汽车在线行驶的距离!因此,当日志出现时(将其放入队列等之后),我们会这样做:


type Delta struct {

    DeviceId string

    time     int64

    Distance float64

}

var LastLogs = make(map[string]FullLog)

var Distances = make(map[string]Delta)



func addLastLog(l FullLog) {

    LastLogs[l.DeviceID] = l

}

func AddToLogPerDay(l FullLog) {

    //mutex.Lock()

    if val, ok := LastLogs[l.DeviceID]; ok {

        if distance, exist := Distances[l.DeviceID]; exist {

            x := computingDistance(val, l)

            Distances[l.DeviceID] = Delta{

                DeviceId: l.DeviceID,

                time:     distance.time + 1,

                Distance: distance.Distance + x,

            }

        } else {

            Distances[l.DeviceID] = Delta{

                DeviceId: l.DeviceID,

                time:     1,

                Distance: 0,

            }

        }

    }

    addLastLog(l)


}

它基本上使用效用函数计算距离!因此,在Distances每个设备中,ID 都映射到了一定距离!现在是问题开始的地方:虽然这个距离被添加到Distances map,我想要一个 go 例程来把这个数据放在数据库中,但是因为有很多设备和很多日志等等,对每个日志进行这个查询并不是一个好主意. 所以我需要每 5 秒执行一次,这意味着每 5 秒尝试清空添加到地图的所有最后距离的列表。我写了这个函数:


func UpdateLogPerDayTable() {

    for {

        for _, distance := range Distances {

            logs := model.HourPerDay{}

            result := services.CarDBProvider.DB.Table(model.HourPerDay{}.TableName()).

    }

}

go utlis.UpdateLogPerDayTable()在另一个 goroutine 上被调用。但是这里有很多问题:

  1. 我不知道如何保护Distances,所以当我将它添加到另一个例程中时,我在其他地方阅读它,一切都很好!(问题是我想使用 go 频道并且不知道该怎么做)

  2. 对于这个问题,我如何安排任务?

  3. 可能我会添加一个 redis 来存储所有在线设备,这样我就可以更快地进行选择查询并更新实际的数据库。还为 redis 添加过期时间,因此如果设备在一段时间内没有发送和数据,它就会消失!我应该把这段代码放在哪里?

对不起,如果我的解释还不够,但我真的需要一些帮助。专门用于代码实现


蝴蝶不菲
浏览 185回答 1
1回答

慕容3067478

Go 有一个非常酷的模式,在多个通道上使用for / select 。这允许您使用超时和最大记录大小来批量写入距离。使用这种模式需要使用通道。首先是将您的距离建模为通道:distances := make(chan Delta)然后你跟踪当前批次var deltas []Delta然后ticker := time.NewTicker(time.Second * 5)var deltas []Deltafor {&nbsp; select {&nbsp; &nbsp; &nbsp;case <-ticker.C:&nbsp; &nbsp; &nbsp; &nbsp; // 5 seconds up flush to db&nbsp; &nbsp; &nbsp; &nbsp; // reset deltas&nbsp; &nbsp; &nbsp;case d := <-distances:&nbsp; &nbsp; &nbsp; &nbsp; deltas = append(deltas, d)&nbsp; &nbsp; &nbsp; &nbsp; if len(deltas) >= maxDeltasPerFlush {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// flush&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// reset deltas&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; }}我不知道如何确保距离,所以当我在另一个例程中添加它时,我在其他地方阅读它,一切都很好!(问题是我想使用 go 频道并且不知道该怎么做)如果您打算保留地图并共享内存,则需要使用互斥(互斥)来保护它,以同步 go 例程之间的访问。使用通道允许您将副本发送到通道,从而无需跨 Delta 对象进行同步。根据您的架构,您还可以创建由通道连接的 go 例程管道,这可以使其只有一个 go 例程(monitor goroutine)正在访问Delta,也无需同步。对于这个问题,我如何安排任务?使用通道作为传递Deltas给不同 go 例程的原语 :)可能我会添加一个 redis 来存储所有在线设备,这样我就可以更快地进行选择查询并更新实际的数据库。还为 redis 添加过期时间,因此如果设备在一段时间内没有发送和数据,它就会消失!我应该把这段代码放在哪里?这取决于您完成的架构。您可以为 select 操作编写一个装饰器,它会先检查 redis,然后再访问数据库。此功能的客户端不必知道这一点。写操作可以以相同的方式完成:写入持久存储,然后使用缓存值和过期时间写回 redis。使用装饰器,客户端不需要知道这一点,他们只需执行读取和写入,缓存逻辑将在装饰器内部实现。有很多方法可以做到这一点,这在很大程度上取决于您的实现在哪里解决。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go