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

2024-01-18

给定 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),而不是线程处理。

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

  1. 您无法控制将创建(或销毁!)以及何时创建(或销毁!)哪些线程(如果一个线程崩溃了怎么办?池将重新创建它,但它将获得什么随机生成器?)。因此,我们无法推断出一种可靠的方式来表示“这个线程”有“这个生成器”,更不用说“第二个”线程有“这个生成器”了,因为甚至可能没有第二个线程(如果每个任务都如此之快怎么办?他们的治疗速度比你派遣他们的速度还要快?)。

  2. 您无法控制何时执行哪些任务。好吧...使用 Executors.newFixedThreadPool,您可以按照提交顺序分派它们,但据您所知,操作系统调度程序可能会将所有优先级赋予线程 1,最终将完成所有工作,并且线程 2 将不执行任何操作(可以是两者之间的任意比例)。

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

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

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

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

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

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

1 是:重构为生产者/消费者(反之亦然)

在你的执行器服务中创建 3 个任务,每个任务都监听一个BlockingQueue(其私人等候名单)并拥有自己的私人发电机。这些是消费者。
让你的主线程成为生产者:当它创建工作单元(曾经是一个Callable在你原来的设计中)编号为N,将其调度到编号为N%3的等待队列。就是这样:每个消费者将按照您希望的顺序按顺序接收自己的数据以进行计算。你们已经达到了“缘分”。

2是:让任务自己派发任务。 (用 hacky 的方式来做)

首先,重构您的可调用对象以链接到它们需要使用的生成器。 然后,在主线程上,构建要为每个生成器运行的任务列表。
从主线程为每个生成器分派第一个任务。
在每个可调用对象的末尾,使可调用对象从其列表中调度下一个工作单元。
不过,请注意不要“将您锁定”,如果您从可调用对象分派可调用对象,请不要等待结果,因为这将阻止可调用对象完成,进而阻止新分派的执行。这是一个僵局。

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

不要从可调用对象内部分派可调用对象,而是通过等待 future 来仅从主线程分派。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 ExecutorService 将任务确定性分配给线程 的相关文章

随机推荐

  • Vista/Win2008 上的关键部分泄漏内存?

    看来在 Vista Windows Server 2008 中大量使用关键部分会导致操作系统无法完全重新获得内存 我们在 Delphi 应用程序中发现了这个问题 这显然是因为使用了 CS API 看这个所以问题 https stackove
  • 将 swf 转换为 mp4

    这个答案没有帮助 将压缩的 swf 转换为 mp4 https stackoverflow com q 20194270 630169 尝试转换 swf 文件 ffmpeg 输出 ffmpeg i GTDS demo new swf GTD
  • 如何使 spring @retryable 可配置?

    我有这段代码 Retryable maxAttempts 3 stateful true include ServiceUnavailableException class exclude URISyntaxException class
  • jquery读取嵌套的json

    我有以下 json 如下所示 我正在尝试读取值 TOP1 TOP2 我有点不确定该怎么做 我正在使用以下内容 但这只会给我一个包含 TOP1 和 TOP2 嵌套对象的对象 如何获取 TOP1 和 TOP2 值 getJSON http lo
  • 将 TArray 类型转换为 X 数组是否安全?

    今天我发现了一个编译器错误 QC 108577 http qc embarcadero com wc qcmain aspx d 108577 以下程序无法编译 program Project1 APPTYPE CONSOLE proced
  • 系统找不到JavaApplicationLauncher

    我正在运行 Mac OS X 版本 12 0 1 也称为 Monterey 我已经安装了 MultiWii 程序来控制我正在构建的无人机 本教程逐步介绍下载 解压 甚至适用于 MacOS 的特殊 chmod 指令 然而 当我尝试运行 Mul
  • 读取、读取部分读取

    我似乎无法在文档中找到有关此内容的信息 The read系统调用文档说它读取的数据可能少于指定的数据 做read尝试阅读几次 我知道fread是一个包装器read 当我调用fread 它是否有可能多次从流中读取 直到它变为 0 或读取指定的
  • 如何围绕轮廓绘制矩形?

    我刚刚开始使用 opencv 我正在尝试制作一个程序 在沙子上的岩石图片周围放置方块 该函数的文档here http docs opencv org modules imgproc doc structural analysis and s
  • 如何将 Vec> 移动到 Vec>>

    我有一个Vec
  • Python 中使用回溯记录异常

    如何记录 Python 异常 try do something except How can I log my exception here complete with its traceback Use logging exception
  • 弹性容器中的等高行

    如您所见 list items在第一个row有相同的height 但第二个项目row有不同的heights 我希望所有物品都有统一的height 有什么方法可以在不付出的情况下实现这一目标固定高度并且只使用flexbox 这是我的code
  • 向上滚动时折叠工具栏图像消失

    我正在尝试实现一个带有大标题图像的折叠工具栏 我希望图像开始时非常大 并且它有效 并且不完全崩溃 这也有效 问题是 当工具栏达到最小可折叠高度时 图像会消失 并褪色为应用程序的主要颜色 我希望图像即使在折叠时也保持可见 另外 后退按钮与图片
  • 可观察集合替换项目

    我有一个ObservableCollection 我可以从集合中添加和删除项目 但我无法替换集合中的现有项目 有一种方法可以替换项目并将其反映在我的绑定组件上 System Collections Specialized NotifyCol
  • TPL 数据流:为什么 EnsureOrdered = false 会破坏此 TransformManyBlock 的并行性?

    我正在研究 TPL 数据流管道 并注意到与排序 并行性相关的一些奇怪行为TransformManyBlocks 也可能适用于其他块 这是我要重现的代码 NET 4 7 2 TPL Dataflow 4 9 0 class Program s
  • 如何删除单元格中第一个空格之后的所有字符?

    我有一个城市名称列表 后跟它们所在的州 所有这些都在 Excel 的一列中 如何删除第一个空格之后的所有内容 以便城市名称成为单元格中唯一保留的内容 示例 A1 约翰逊德克萨斯州 应该只是 A1 约翰逊 我假设你想要一个 VBA 解决方案
  • 使用 vscode 扩展 API 更改文件 EOL

    我可以在 VSCode 中默默地更改行尾顺序吗 像这样的事情 vscode commands executeCommand workbench action editor changeEOL LF 您可以将此行添加到您的用户首选项设置中 C
  • 应用按键后捕获 HTML 文本输入按键?

    完全是新手问题 所以请耐心等待 当 HTML 文本输入控件中发生按键时 有两个事件似乎对管理按键很有用 onKeyPress 和 onChanged onKeyPress 在按下按键后 但在将操作应用于控件的文本之前触发 仅当焦点从控件中移
  • 使用 webdriver 查找元素的最佳且最快的方法是什么? By.XPath 或 By.ID 还是其他?为什么? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 使用 webdriver 查找元素的最佳且最快的方法是什么 By XPath 或 By ID 还是其他 为什么 我在很多地方都读到 XPath
  • 替换字符串中的字符,而不使用字符串replace()方法[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一个字符串 String originalString This car is my car 我想用 自行车 替换 汽车 而不使用字符
  • 使用 ExecutorService 将任务确定性分配给线程

    给定 Executor 服务具有固定的线程池 是否可以保证任务到线程的确定性分配 更准确地说 假设只有两个线程 即 pool thread 0 和 pool thread 1 并且有 2 个要执行的任务的集合 我希望实现的是前一个线程始终执