当前位置: 首页 > 知识库问答 >
问题:

ForkJoinpool在调用期间停顿所有/加入

长孙弘盛
2023-03-14

我尝试使用ForkJoinpool来并行化我的CPU密集型计算。我对ForkJoinpool的理解是,只要有任何任务可以执行,它就会继续工作。不幸的是,我经常观察到工作线程空闲/等待,因此不是所有的CPU都保持忙碌。有时我甚至观察到额外的工作线程。

我没有想到这一点,因为我严格尝试使用非阻塞任务。我的观察与ForkJoinpool的观察非常相似,似乎浪费了一根线。在调试了很多到ForkJoinpool我有一个猜测:

我使用invokeAll()将工作分配到一系列子任务上。invokeAll()完成执行第一个任务后,它开始加入其他任务。这可以正常工作,直到下一个要加入的任务位于执行队列的顶部。不幸的是,我异步提交了其他任务,但没有加入它们。我希望ForkJoin框架首先继续执行这些任务,然后再返回到加入任何剩余的任务。

但它似乎不是这样工作的。相反,在等待的任务准备就绪(可能是由其他工作线程执行)之前,工作线程会停滞不前地调用等待()。我没有验证这一点,但它似乎是调用连接()的一般缺陷。

ForkJoinPool提供了一个asyncMode,但这是一个全局参数,不能用于单个提交。但我喜欢看到我的异步分叉任务很快就会执行。

那么,为什么我们要完成这项任务呢。doJoin()不只是在队列顶部执行任何可用的任务,直到它准备就绪为止(要么由自己执行,要么被他人窃取)?

共有3个答案

商天逸
2023-03-14

您使用此框架的目的并不是非常狭隘的。

该框架始于2000年研究论文中的实验。从那以后,它被修改了,但是基本设计,大型数组上的分叉和连接,保持不变。基本目的是教本科生如何从平衡树的叶子上走下来。当人们将它用于其他简单的数组处理时,奇怪的事情就会发生。它在Java7中做的事情超出了我的能力;这就是本文的目的。

问题只会在Java8中变得更糟。这是驱动所有流并行工作的引擎。阅读那篇文章的第二部分。lambda兴趣列表中充满了线程停顿、堆栈溢出和内存溢出错误的报告。

如果您不将其用于大型数据结构的纯递归分解,请自担风险。即便如此,它创建的过多线程也会造成严重破坏。我不会再继续讨论这个问题了。

东方国安
2023-03-14

由于似乎没有其他人理解我的问题,因此我尝试解释几晚调试后的发现:

如果所有fork/join调用都严格成对,那么ForkJoinTasks的当前实现工作得很好。用一个开括号表示叉,用一个闭括号表示叉,完美的二进制叉连接模式可能如下所示:

{([][]) ([][])} {([][]) ([][])}

如果使用invokeAll(),还可以提交如下子任务列表:

{([][][][]) ([][][][]) ([][][][])}

然而,我所做的看起来像这样的模式:

{([) ([)} ... ]]

您可能会认为这看起来很糟糕,或者是对fork-join框架的滥用。但唯一的限制是,任务完成依赖项是非循环的,否则您可能会遇到死锁。只要我的[]任务不依赖于()任务,我就看不出有任何问题。冒犯的]]只是表示我没有明确地等待他们;他们可能有一天会结束,这对我(那时)来说并不重要。

实际上,当前的实现能够执行我的互锁任务,但只能通过生成额外的助手线程来执行,这是非常低效的。

该漏洞似乎是当前的join()实现:加入一个)期望看到其相应的(在其执行队列的顶部,但它找到了一个[并感到困惑。当前线程不是简单地执行[]来摆脱它,而是挂起(调用等待()),直到其他人来执行意外的任务。这会导致严重的html" target="_blank">性能故障。

我的主要目的是在队列上增加额外的工作,以防止工作线程在队列为空时挂起。不幸的是,情况恰恰相反:-(

毕富
2023-03-14

你是死对加入()。我两年前写了这篇文章,指出了连接()的问题。

正如我在这里所说的,在完成之前的请求之前,框架无法执行新提交的请求。并且每个工作线程在其当前请求完成之前无法窃取,这将导致等待()。

您看到的其他线程是“延续线程”由于join()最终会发出wait(),因此需要这些线程,这样整个框架就不会暂停。

 类似资料:
  • 代码: 我有上面的代码来并行执行一些任务。考虑到已经让调用线程等待完成,不知道它是否应该是而不是块中的。 注意:仅从输入列表中读取。

  • 问题内容: 我只能修改ajax调用中的代码。当我单击名为的表单中的提交时,就会发生ajax调用。 问题答案: 您需要在成功处理程序之前将其停止。因为该函数在您的AJAX调用之后完成执行,所以表单将在进行ajax调用时提交(并且在您的ajax调用结束时为时已晚)。 但是,可以,在函数末尾放置return false。 如果在成功处理程序中确实很重要,则可以同步执行调用。

  • 我有几个带有日期的表,我正试图加入这些表来制作一个大表,其中数据按日期分组。 我现在通过左键联接来完成这个任务,从我需要联接的表中生成子选择(其中许多是同一个表,具有不同的查询位置,涉及SUM和COUNT,所以我认为我必须使用子选择)。我遇到的问题是,如果第一个表中没有一个日期,那么即使后续表中有一些行与该日期联接,它也不会显示在表中。我是根据DATE(datetime_column)联接的。 所

  • 即便用追踪式收集辅助引用计数,在很多时候停顿时间依然不可接受,原因有几点: 虽然引用计数可以保证不产生循环的对象能实时回收,但存在很多这类对象是挂在循环引用数据结构上的,比如两个对象互相引用,而他们各自又挂了很多int和string数据,即是一个例子。由于这些对象的回收也是在停顿期,实时性不是那么好 循环引用本身并不罕见,双链表、树等数据结构都存在这种情况,实际工作中很多复杂的业务也存在这种情况,

  • 我对和的内部调度机制有点困惑。 同时,显示为不同的,因为它使用了工作窃取算法。如果我理解正确,它意味着一个线程可以从另一个线程窃取一些任务。 然而,我并不真正理解和中实现的机制之间的区别。从我的理解来看,两种机制都应该尽可能减少每个线程的空闲时间。 如果在的情况下,每个线程都有自己的队列,我会理解的。然而,情况并非如此,因为队列是由池的不同线程共享的。