Go 戈朗延迟有时会失败

Go 戈朗延迟有时会失败,go,mutex,Go,Mutex,我正在处理一个具有多个例程的应用程序。处理器接收一个ID(字符串)并执行一些操作。ID可能是重复的,我不希望在另一个例程处理ID时有多个例程来处理它 我使用了一个同步互斥映射 type cache struct{ sync.Mutex ids map[string]struct{} } func(c *cache) addIfNotPresent(string)bool{ c.Lock() defer c.Unlock() if _, ok := c.ids[id

我正在处理一个具有多个例程的应用程序。处理器接收一个ID(字符串)并执行一些操作。ID可能是重复的,我不希望在另一个例程处理ID时有多个例程来处理它

我使用了一个同步互斥映射

type cache struct{
   sync.Mutex
   ids map[string]struct{}
}

func(c *cache) addIfNotPresent(string)bool{
   c.Lock()
   defer c.Unlock()
   if _, ok := c.ids[id]; ok{
       return false
   }
   c.ids[id] = struct{}{}
   return true
}
func(c *cache) delete(string){
   c.Lock()
   defer c.Unlock()
   delete(c.ids, id)
}
我的处理器有此映射的一个实例。现在我的过程看起来像这样

func process(string){
   ok := cache.addIfNotPresent(id)
   if !ok{
      return
   }
   defer cache.delete(id)

   ctx, cancel := context.WithTimeout(context.Background(), timeout)
   defer cancel()
   err := doOne(ctx)
   if err {
      return err
   }
   ...
   return nil
}
使用defer,这样无论处理器中发生什么,id都会被删除

有时(并非总是)该值不会从映射中逐出。从日志/指标中,我确定这不是错误案例,但流程功能已完成,并且密钥未从映射中逐出

这里是否缺少互斥或延迟的任何行为

有时(并非总是)该值不会从映射中逐出

你什么时候怎么检查


您粘贴的函数的代码在完成时实际上正在删除该键,但在该执行完成后,可能会有另一个goroutine处理(从而再次添加)相同的键。

我可以看到此代码导致度量函数(在
过程中检查
ok
后执行)要查看地图中的值,请执行以下操作:

  • thread1将值添加到映射并开始处理它。它产生CPU
  • thread2进入
    进程
    函数,调用
    addIfNotPresent
    并获取
    false
    。它在进入
    if!之前产生CPU!好{
  • thread1获取CPU,完成其工作,从映射中删除值并退出
  • thread3获取值,检查映射,发现值不再存在,开始处理它。它产生CPU
  • thread2获取CPU,检查
    ok
    并看到它是
    false
    。此时它会命中度量函数。度量函数检查映射并看到其中的值。随后会出现混淆

  • 验证这一点的一种方法是在处理完成后检查映射。如果我的假设是正确的,您将不会看到任何值仍然留在那里。如果我错了,并且这些值确实没有被正确逐出,您仍然会在那里看到它们。

    至少您应该发布
    addIfNotPresent()
    delete()
    实现。代码看起来不错。你确定你发布了实际的代码吗?缺少arg名称,这让我觉得我们在这里看到的可能缺少更多。我没有复制粘贴。但我确信这正是我所拥有的。证据表明并非如此。defer()没有失败。我编辑了一点。
    doOne
    函数接受一个上下文。我通过超时传递一个上下文,并推迟取消。这有什么区别吗?这是一个POC系统,很容易通过日志/度量来跟踪ID。在
    过程
    函数中检查
    ok
    后,我有一个度量/日志。该度量已被获取现在以固定的间隔(间隔与传递到函数中的id相同)删除一次命中大约一天。通过其他指标,我可以看到唯一的可能性是没有删除密钥。