如何并行化GPars Actors?

时间:2014-12-05 21:32:03

标签: multithreading groovy actor gpars

我对GPars Actors的理解可能已经关闭,所以如果我错了请纠正我。我有一个Groovy应用程序,可以轮询Web服务以获取作业。当找到一个或多个作业时,它会将每个作业发送到我创建的DynamicDispatchActor,并处理作业。这些作业完全是自包含的,不需要向主线程返回任何内容。当多个作业同时进入时,我希望它们能够并行处理,但不管我采用什么配置,演员都会先进行处理。

举一个代码示例:

def poolGroup = new DefaultPGroup(new DefaultPool(true, 5))

def actor = poolGroup.messageHandler {
    when {Integer msg -> 
        println("I'm number ${msg} on thread ${Thread.currentThread().name}")
        Thread.sleep(1000)
    }
}

def integers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

integers.each {
    actor << it
}

打印出来:

I'm number 1 on thread Actor Thread 31
I'm number 2 on thread Actor Thread 31
I'm number 3 on thread Actor Thread 31
I'm number 4 on thread Actor Thread 31
I'm number 5 on thread Actor Thread 31
I'm number 6 on thread Actor Thread 31
I'm number 7 on thread Actor Thread 31
I'm number 8 on thread Actor Thread 31
I'm number 9 on thread Actor Thread 31
I'm number 10 on thread Actor Thread 31

每次打印之间略有停顿。另请注意,每个打印输出都来自同一个Actor /线程。

我想在这里看到的是前5个数字是立即打印出来的,因为线程池设置为5,然后接下来的5个数字作为这些线程释放。我完全不在这里吗?

1 个答案:

答案 0 :(得分:3)

为了让它按预期运行,几乎没有变化:

import groovyx.gpars.group.DefaultPGroup
import groovyx.gpars.scheduler.DefaultPool

def poolGroup = new DefaultPGroup(new DefaultPool(true, 5))

def closure = {
    when {Integer msg ->
        println("I'm number ${msg} on thread ${Thread.currentThread().name}")
        Thread.sleep(1000)
        stop()
    }
}

def integers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def actors = integers.collect { poolGroup.messageHandler(closure) << it }
actors*.join()

完整的主要档案:https://gist.github.com/wololock/7f1348e04f68710e42d2

然后输出将是:

I'm number 5 on thread Actor Thread 5
I'm number 4 on thread Actor Thread 4
I'm number 1 on thread Actor Thread 1
I'm number 3 on thread Actor Thread 3
I'm number 2 on thread Actor Thread 2
I'm number 6 on thread Actor Thread 3
I'm number 9 on thread Actor Thread 4
I'm number 7 on thread Actor Thread 2
I'm number 8 on thread Actor Thread 5
I'm number 10 on thread Actor Thread 1

现在让我们来看看发生了什么变化。首先,在您之前的示例中,您只对一个演员工作过。您正确定义了poolGroup,但随后您创建了一个actor并将计算转移到此单个实例。要使这些计算并行运行,您必须依赖poolGroup并仅将输入发送到某个消息处理程序 - 池组将处理actor创建及其生命周期管理。这就是我们所做的:

def actors = integers.collect { poolGroup.messageHandler(closure) << it }

它将创建一个以给定输入开头的actor集合。池组将注意不超过指定的池大小。然后你必须join每个演员,这可以通过使用groovy的魔法来完成:actors*.join()。感谢应用程序将等待终止,直到所有参与者停止计算。这就是为什么我们必须将stop()方法添加到消息处理程序正文的when闭包中 - 没有它,它不会终止,因为池组不知道演员是否做到了工作 - 他们可能会等待,例如对于另一条消息。

替代解决方案

我们还可以考虑使用GPars并行迭代的替代解决方案:

import groovyx.gpars.GParsPool

// This example is dummy, but let's assume that this processor is
// stateless and shared between threads component.
class Processor {
    void process(int number) {
        println "${Thread.currentThread().name} starting with number ${number}"
        Thread.sleep(1000)
    }
}

def integers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Processor processor = new Processor()

GParsPool.withPool 5, {
    integers.eachParallel { processor.process(it) }
}

在此示例中,您使用无状态组件Processor并使用具有多个输入值的无状态Processor的一个实例进行并行计算。

我试图找出你在评论中提到的案例,但我不确定单个演员是否可以一次处理多条消息。行为者的无国籍状态仅意味着它在处理消息期间不会改变它的内部状态,并且不得在行动者范围内存储任何其他信息。如果我的推理不正确,有人可以纠正我会很棒:)

我希望这会对你有所帮助。最好!