ForkJoinPool调度与ExecutorService [英] ForkJoinPool scheduling vs ExecutorService

查看:710
本文介绍了ForkJoinPool调度与ExecutorService的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对ExecutorService和ForkJoinPool的内部调度机制感到有些困惑。

I'm slightly confused by the internal scheduling mechanism of the ExecutorService and the ForkJoinPool.

我理解ExecutorService调度已完成这样

I understood the ExecutorService scheduling is done this way.

一堆任务排队。一旦线程可用,它将处理第一个可用任务,依此类推。

A bunch of tasks are queued. Once a thread is available it will handle the first available task and so forth.

同时,ForkJoinPool呈现为不同,因为它使用了工作窃取算法。如果我理解正确,这意味着一个线程可以从另一个线程窃取一些任务。

Meanwhile, a ForkJoinPool is presented as distinct because it uses a work-stealing algorithm. If I understand correctly it means a thread can steal some tasks from another thread.

然而,我并不真正理解与ExecutorService中实现的机制的区别。根据我的理解,这两种机制都应该允许尽可能地减少每个线程的空闲时间。

Yet, I don't really understand the difference with the mechanism implemented in the ExecutorService. From my understanding, both mechanisms should allow to reduce as most as possible the idle time of each threads.

我会理解,如果在ExecutorService的情况下,每个线程都会有自己的队列。然而,事实并非如此,因为队列由池的不同线程共享...

I would understand if in the case of an ExecutorService, each thread would have its own queue. Yet, it is not the case as the queue is shared by the different threads of the pool...

任何澄清都非常受欢迎!

Any clarification would be more than welcome!

推荐答案

假设你有一大堆整数,你想要添加所有这些。使用 ExecutorService ,您可能会说:让我们将该数组划分为大小数量的线程数/ 4.所以如果你有一个包含160个元素的数组(并且你有4个CPU的数组) ),您创建 120/4/4 = 10 ,因此您将创建16个块,每个块保持10个整数。创建runnables / callables并将它们提交给执行者服务(当然,想到一种方法可以在完成后合并这些结果)。

Suppose you have a very big array of ints and you want to add all of them. With an ExecutorService you might say: let's divide that array into chunks of let's say number of threads / 4. So if you have an array of 160 elements (and you have 4 CPU's), you create 120 / 4 / 4 = 10, so you would create 16 chunks each holding 10 ints. Create runnables/callables and submit those to an executor service (and of course think of a way to merge those results once they are done).

现在你的希望是每个CPU将执行其中4个任务并对其进行操作。现在让我们假设有些数字非常复杂(当然不是,但请耐心等待),可能会发现3个线程/ CPU完成了他们的工作,而其中一个只忙于第一个块。当然,没有人想要这样,但可能会发生。现在最糟糕的是你无法做任何事情。

Now your hopes are that each of the CPU's will take 4 of those tasks and work on them. Now let's also suppose that some of the numbers are very complicated to add (of course not, but bear with me), it could turn out that 3 threads/CPUs are done with their work while one of them is busy only with the first chunk. No one wants that, of course, but could happen. The bad thing now is that you can't do anything about it.

什么是 ForkJoinPool 而不是说提供我想你如何分割你的任务和我必须做的最小工作量的实现,我会处理其余的事情。在 Stream API 中,这是通过 Spliterator s完成的;主要有两种方法 trySplit (返回 null 意味着什么都不能拆分或新 Spliterator - 意味着一个新的块)和 forEachRemaning ,一旦你不能再拆分你的任务,它将处理元素。这就是偷工作会帮助你的地方。

What ForkJoinPool does instead is say provide me with how you want to split your task and the implementation for the minimal workload I have to do and I'll take care of the rest. In the Stream API this is done with Spliterators; mainly with two methods trySplit (that either returns null meaning nothing can be split more or a new Spliterator - meaning a new chunk) and forEachRemaning that will process elements once you can't split your task anymore. And this is where work stealing will help you.

你说如何计算你的块(通常分成两半)以及当你不能再拆分时该怎么做。 ForkJoinPool 会将第一个块分派给所有线程,当其中一些线程空闲时 - 它们完成了他们的工作,他们可以从其他线程查询其他队列,看看他们是否有工作。如果他们注意到其他一些线程队列中有块,他们会接受它们,将它们自行拆分并处理它们。它甚至可以证明他们不会自己完成整个工作 - 其他一些线程现在可以查询这个线程的队列,并注意到还有工作要做等等......这要好得多现在,当这3个线程空闲时,他们可以接受其他一些工作 - 而且所有这些工作都很忙。

You say how are your chunks are computed (usually split in half) and what to do when you can't split anymore. ForkJoinPool will dispatch the first chunk to all threads and when some of them are free - they are done with their work, they can query other queues from other threads and see if they have work. If they notice that there are chunks in some other threads queues, they will take them, split them on their own and work on those. It can even turn out that they don't do the entire work on that chunks on their own - some other thread can now query this thread's queue and notice that there is still work to do and so on... This is far better as now, when those 3 threads are free they can pick up some other work to do - and all of them are busy.

这个例子有点简化,但距离现实并不遥远。只是你需要比工作窃取工作的CPU /线程更多的块;因此通常 trySplit 必须有一个智能实现,你需要在你的流源中有很多元素。

This example is a bit simplified, but is not very far from reality. It's just that you need to have a lot more chunks than CPU's/threads for work stealing to work; thus usually trySplit has to have a smart implementation and you need lots of elements in the source of your stream.

这篇关于ForkJoinPool调度与ExecutorService的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆