草庐IT

dictionary - Go中有类似Java的ConcurrentMap.computeIfAbsent的功能吗?

coder 2023-06-30 原文

我试图在 Go 的标准库和许多其他类似于 Java 的 ConcurrentMap.computeIfAbsent 的缓存库中找到这个函数。 我在标准库中找到了 sync.Map ,它看起来像我正在寻找的东西。我想使用 sync.Map 作为并发映射。问题在于以下函数不像 Java 的 ConcurrentMap 那样提供延迟计算。

func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)

LoadOrStore returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored.

它是原子的,但是这个函数对我来说没有意义。我不明白它的用途,因为我是这门语言的新手。我不知道为什么第二个参数是一个值而不是一个函数。由于将急切地评估参数,所以我不知道每次重新计算值时这个函数有什么用。

如果函数签名像这样,IMO 会更有用

func (m *Map) LoadOrStore(key interface{}, f func() interface{}) (actual interface{}, loaded bool)

我的意思是,当我们已经有了值时,为什么还需要 map ? 很确定我错过了什么。

让我详细说明一下。我不明白为什么函数将值作为参数而不是函数的原因,我很确定我错过了一些东西。我想以并发方式从 map 中获取值。当键不存在时,我想计算值并自动将其放入 map 中。我知道我不应该将它与 Java 中的 ConcurrentMap 进行比较,因为语言和范例不同,但是说出我来自哪里可能很有用,并且它可以使任何正在学习 Go 的人更好地理解它关于这个区别。

Java ConcurrentMap 中有类似的功能。

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.

问题是为什么LoadOrStore函数采用值而不是函数?任何关于为什么以这种方式设计 API 的见解将不胜感激。还有一种方法可以完成与我在 Java 中所做的相同的事情 computeIfAbsent不在 map 周围使用明确的锁?

已更新 我发现修改 sync.Map.LoadOrStore 以获取函数而不是值非常容易。

https://play.golang.org/p/VBIaS8ZV38o

但不确定它是否会按预期工作。

最佳答案

目的sync.Map 不是提供延迟值计算的方法,而是提供通用的map[interface{}]接口(interface){}并发使用安全 通过多个协程。如果没有额外/显式同步,go 中的 map 类型对于并发使用是不安全的。

这在sync.Map 的文档中有明确说明:

Map is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple goroutines without additional locking or coordination. Loads, stores, and deletes run in amortized constant time.

The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

The Map type is optimized for two common use cases: (1) when the entry for a given key is only ever written once but read many times, as in caches that only grow, or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.

至于为什么sync.Map有一个

LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)

方法代替:

LoadOrComputeAndStore(key interface{}, f func() interface{}) (actual interface{}, loaded bool)

该问题是基于意见的,因此与 Stackoverflow 无关。但出于一个原因:前者更易于使用(当你已经有了值时,你不必使用函数文字)而后者并不总是需要的(即使在某些情况下确实很有用)。

请注意,我们可以轻松创建具有此“功能”的自定义同步映射:

type MyMap struct {
    sync.Map
}

func (m *MyMap) LoadOrComputeAndStore(key interface{}, f func() interface{}) (actual interface{}, loaded bool) {
    actual, loaded = m.Load(key)
    if loaded {
        return
    }
    return m.LoadOrStore(key, f())
}

使用示例:

m := &MyMap{Map: sync.Map{}}

f := func() interface{} {
    fmt.Println("calculating...")
    return "myvalue"
}
key := "mykey"

fmt.Println(m.LoadOrComputeAndStore(key, f))
fmt.Println(m.LoadOrComputeAndStore(key, f))

输出(在 Go Playground 上尝试):

calculating...
myvalue false
myvalue true

如输出所示,f() 仅被调用一次。第一次调用 m.LoadOrComputeAndStore() 调用 f(),并报告 loaded=false(因为 key 是尚未在 map 中)。第二次调用 m.LoadOrComputeAndStore() 不会调用 f(),并报告 loaded=true

请注意,这是实现您想要的目标的“简单且充分”的方法。但它不是“防弹”,这意味着 LoadOrComputeAndStore() 的实现并不能保证 f() 只能被调用一次。这是因为它首先使用 Map.Load() 加载值。如果它发现它不在 map 中,那么它会调用 f(),在此期间,并发 goroutine 可能会执行相同的操作。因此,如果 f() 需要很长时间才能返回并且 map 被多个 goroutines 使用,则 f() 可能会被调用多次。如果需要保证避免多次 f() 调用,那么您应该使用显式 sync.RWMutex在 map 上。

总结一下:上面的示例实现在大多数情况下应该足够了,即当 f() 没有副作用时,您只需要 LoadOrComputeAndStore() 以避免由于执行时间长而不得不调用 f()。如果 f() 确实有一个不能重复的副作用,那么这个实现是不充分的,需要手动锁定。

关于dictionary - Go中有类似Java的ConcurrentMap.computeIfAbsent的功能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49528232/

有关dictionary - Go中有类似Java的ConcurrentMap.computeIfAbsent的功能吗?的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  3. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  4. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  5. ruby - ruby 中有 each_if 吗? - 2

    假设我在Ruby中有这个each循环。@list.each{|i|putsiifi>10breakend}我想循环遍历列表直到满足条件。这让我感到“不像Ruby”,因为我是Ruby的新手,是否有Ruby方法可以做到这一点? 最佳答案 您可以使用Enumerable#detect或Enumerable#take_while,取决于您想要的结果。@list.detect{|i|putsii>10}#Returnsthefirstelementgreaterthan10,ornil.正如其他人所指出的,更好的风格是先进行子选择,然后再对其

  6. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  7. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

随机推荐