Grails 执行器插件 callAsync block 始终采用相同的值

标签 grails threadpool java.util.concurrent executor

我正在尝试在 grails 中使用执行器插件,但遇到了一个无法解决的问题。 基本上,我有一个要抓取的链接列表,但遇到的问题是它总是抓取相同的链接,因此我将示例简化为:

    List offerLinks = getOfferLinks(parser)
    offerLinks.each{println it}

    List futures = new Vector()
    for (def link : offerLinks) {
        def future = callAsync {
            return link
        }
        futures.add(future)
    }

    futures.each{println "FUTURE " +  it.get()}

这是控制台中打印的内容

bt-ofrd-acciona-6633344.htm?
bt-ofrd-celiasiffredi-293068.htm?
bt-ofrd-clahubiz-92924.htm?
bt-ofrd-haruko-1672632.htm?
future bt-ofrd-clahubiz-92924.htm?
future bt-ofrd-haruko-1672632.htm?
future bt-ofrd-haruko-1672632.htm?
future bt-ofrd-haruko-1672632.htm?

前 4 个结果针对 offerLinks.each{println it} 代码
最后 4 个用于 futures.each{println "FUTURE "+ it.get()}

我试图找出的是为什么将这些链接放入 callAsync block 并从 future 对象中检索它们会使它们采用最后一个值,看起来它会替换已经创建的 future 对象?

这段代码位于 Controller 调用的服务内部。 我很感激你能给我的任何帮助。谢谢

更新:
我认为 Java 执行器 API 中存在某种问题......或者也许我不完全理解它的真正工作原理?
这是另一个测试,更改代码以使用 invokeAll:

    def threadPool = Executors.newCachedThreadPool()

    List offerLinks = getOfferLinks(parser)
    List lista = new ArrayList()
    for (enlace in offerLinks) {
        println "link " + enlace
        lista.add({enlace} as Callable)
    }
    def futures = threadPool.invokeAll(lista)

    futures.each{println "FUTURE " +  it.get()}

这就是打印的内容
链接/bt-ofrd-implementar-192996.htm?
链接/bt-ofrd-cdonini-864908.htm?
链接/bt-ofrd-hvtalent-1493932.htm?
链接/bt-ofrd-dbak-1358120.htm?
链接/bt-ofrd-hexacta-100072.htm?
链接/bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?
future /bt-ofrd-ccibelli-457472.htm?

最佳答案

在我看来,在闭包外部定义的变量范围发生了一些奇怪的事情,但从内部引用,它没有正确“关闭”。如果这样做的话效果会更好吗

def threadPool = Executors.newCachedThreadPool()

List offerLinks = getOfferLinks(parser)
List lista = new ArrayList()
for (enlace in offerLinks) {
    println "link " + enlace
    lista.add(({ it }.curry(enlace)) as Callable)
}
def futures = threadPool.invokeAll(lista)

futures.each{println "FUTURE " +  it.get()}

这应该确保正确的东西被传递到闭包中,并且闭包本身不需要直接引用外部定义的enlace变量。

这本身并不能解释为什么您已经尝试过的方法不起作用,但它可能会为您提供解决方法。


编辑:我以前没有发现这一点,但我现在注意到您没有在 for 循环中声明 enlace,因此它不是局部变量,并且闭包(正确地)引用单个共享变量,而不是“关闭”特定循环迭代中的值。如果您使用这样的结构,它应该可以工作:

def tasks = offerLinks.collect { link ->
  println "link " + enlace
  return ({ link } as Callable)
}
def futures = threadPool.invokeAll(tasks)
futures.each{println "FUTURE " +  it.get()}

其中 link 变量是 collect 闭包的本地变量,因此 {...} as Callable 将在正确的值上关闭。 callAsync 的等效方法是使用

List futures = offerLinks.collect { link ->
  callAsync { link }
}

关于Grails 执行器插件 callAsync block 始终采用相同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15302909/

相关文章:

grails - Grails findAll在 'each'闭包内返回空属性

java - 使用单线程 ForkJoinPool 在 Future 内运行 Future.map

java - 如何获得固定大小的队列

java - 如何有效地为很多重复的表保存值(value)?

oracle - Grails - ORA-02275 - 这样的引用约束已经存在

tomcat - Grails部署数据问题

grails - grails自定义标签将html标签作为字符串文字

java - 从不同线程的多个任务中检索数据

c# - ThreadPool.QueueUserWorkItem 是线程安全的吗?

java - 如何在 Spring Boot @Async 中使用 ForkJoinPool?