2

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

    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?
未来的 bt-ofrd-clahubiz-92924.htm?
未来的 bt-ofrd-haruko-1672632.htm?
未来的 bt-ofrd-haruko-1672632.htm?
未来的 bt-ofrd-haruko-1672632.htm?

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

我试图找出的是为什么将这些链接放在 callAsync 块中并从未来对象中检索它们会使它们获取最后一个值,似乎它替换了已经创建的未来对象?

这段代码位于控制器调用的服务中。我很感激你能给我的任何帮助。谢谢

更新
我认为 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?
未来 /bt-ofrd-ccibelli-457472.htm?
未来 /bt-ofrd-ccibelli-457472.htm?
未来 /bt-ofrd-ccibelli-457472.htm?
未来 /bt-ofrd-ccibelli-457472.htm?
未来 /bt-ofrd-ccibelli-457472.htm?
未来 /bt-ofrd-ccibelli-457472.htm?

4

3 回答 3

2

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

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变量。

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


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

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 }
}
于 2013-03-11T17:51:18.777 回答
0

听起来像我刚刚经历的同一个现象,请参阅这个答案

对我来说,问题是“当前线程的 MDC 被新生成的线程继承”。我不知道为什么会这样,所以我不知道你为什么会遇到同样的问题——但可能是因为你从服务中检索了链接?

于 2013-03-09T06:34:20.110 回答
0

这是否更好?

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

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

) }

futures.each{println "FUTURE " +  it.get()}
于 2013-03-08T20:52:19.637 回答