繁体   English   中英

使用ExecutorService确定将任务分配给线程

[英]Deterministic assignment of tasks to threads using ExecutorService

给定具有固定线程池的Executor服务,是否可以保证线程的确定性任务分配? 更确切地说,假设只有两个线程,即pool-thread-0和pool-thread-1,并且有一组要执行的2个任务。 我希望实现的是前一个线程总是执行第一个,而后者处理剩下的一个。

这是一个例子:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService executorService = newFixedThreadPool(2,
            new ThreadFactoryBuilder().setNameFormat("pool-thread-%d").build());

    for (int i = 0; i < 5; i++) {
        List<Callable<Integer>> callables = ImmutableList.of(createCallable(1), createCallable(2));
        executorService.invokeAll(callables);

    }
}

public static Callable<Integer> createCallable(final int task) {
    return new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            currentThread().sleep(1000);
            System.out.println(Thread.currentThread().getName() + " executes task num: " + task);
            return task;
        }
    };
}

我机器的示例输出:

pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2

简而言之,我希望确保pool-thread-0始终执行第一个任务。 任何帮助将不胜感激!

ExecutorService的目的不是为其Callable / Runnable提供“线程关联”。 有人可能会说“这就是重点”,API可以让程序员处理工作描述(Callable),而不是处理Thread处理。

您的设计,“有一个与每个线程关联的随机数据生成器”的地方不适合ExecutorService,原因有三个:

  1. 你无法控制将创建(或销毁!)的线程以及什么时候(如果一个崩溃会怎样?池会重新创建它但它会得到什么随机生成器?)。 所以我们无法推断一种可靠的方式来说“这个线程”有“这个生成器”,更不用说“第二个”线程有“这个生成器”,因为甚至可能没有第二个线程(如果每个任务都如此之快,它们的处理速度比你发送的要快吗?

  2. 您无法控制何时执行哪些任务。 好吧......使用Executors.newFixedThreadPool,你可以按照提交的顺序调度它们,但是据你所知,OS调度程序可能会优先考虑线程1,最终会完成所有的工作,并且线程2根本不做任何事情(它可以是任何比例)。

  3. 将“数据生成器”传递给线程的唯一方法是覆盖执行程序服务的ThreadFactory。 否则,您无法访问线程实例(运行时来自可调用的appart)。 因此,要将特定生成器关联到特定线程,您必须知道当前正在创建哪个线程编号,如果您计算线程,这很容易,但如果您想知道什么是Callable,则很难线程旨在(见第2点)。

因此,我强烈建议您定义一些将工作单元与数据生成器关联的其他方法,因为“线程实例”通常不可靠 - 至少不通过Executor Service。 例如,当你说

我需要提供它们处理的线程和数据的组合是可重复的。

我知道您将始终发送一定数量的Callables,并且您需要每个Callables处理由特定生成器发出的特定数据集。 假设我们有一定数量的任务和3个生成器,任务(N)将使用生成器N%3

为了使结果可重复,您还需要使用相同生成器的任务不会同时执行(您希望通过线程关联实现什么?)。

有一定数量的模式可以实现这一点。

1是:对生产者/消费者的重构(反过来做)

在您的执行程序服务中创建3个任务,每个任务侦听一个BlockingQueue (其私有等待列表)并拥有自己的私有生成器。 这些是消费者。
使您的主线程成为生产者:当它创建工作单位(原始设计中曾经是可Callable )数字N时,将其分配给等待队列号N%3。 就是这样:每个消费者将按顺序依次接收自己的数据,按照您希望的顺序进行计算。 你已经达到了“亲和力”。

2是:自己完成任务调度任务。 (这是hacky方式)

首先,重构您的callables以获得他们需要使用的生成器的链接。 然后,在主线程上,构建要为每个生成器运行的任务列表。
从主线程调度每个生成器的第一个任务。
并且每个callable的结束,使Callable从其列表中调度下一个工作单元。
但是,如果你从callables中调用callables,请不要等待结果,因为这会阻止Callables完成,这反过来会阻止新调度执行。 这是一个僵局。

3:与2相同,效率较低,但风险较小

而不是从callables内部调度Callables,只通过等待期货从你的主线程发送。

执行这两种方法中的任何一种,您无法保证哪些任务将首先完成或最后完成,但您可以保证您发送的工作单元可预测地与您控制的数据生成器相关联,并且它们将按您发送的顺序执行他们。 希望这足够了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM