![](/img/trans.png)
[英]Using ExecutorService to repeatedly perform a number of similar tasks in parallel
[英]Using ExecutorService with a tree of tasks to perform
我们遇到了一些问题。 :)
我们希望确保只有N个线程可以随时执行后台任务。 为此,我们使用了一个固定的线程池执行器。 它似乎工作正常。
然后我们发现了一个问题。 假设你有一个类,它使用执行程序做一些并行工作,然后在执行程序线程中调用其他类,这也执行一些并行工作,打算等待它。 这是发生的事情:
嗯,哦。 所以在这一点上,所有四个线程现在都在等待任务完成,但它们正在协作阻止执行者实际运行这些任务。
此问题的解决方案1如下:在向执行程序提交新任务时,如果我们已经在运行所有线程,并且我们已经在其中一个执行程序线程上运行,则运行内联任务。 这个工作正常10个月,但现在我们遇到了问题。 如果它提交的新任务仍然相对较大,那么您可能会遇到新任务阻止该方法将其他任务添加到队列的情况,否则其他工作线程可以接收该任务。 因此,当线程正在处理内联工作时,会出现大量延迟。
是否有更好的解决方案来执行可能无限制的后台任务树的核心问题? 我知道.NET等同于执行程序服务具有从队列中窃取的某种内置能力,这可以防止发生原始死锁问题,据我所知,这是一种理想的解决方案。 但是在Java土地上呢?
Java 7具有ForkJoinPool
的概念,允许任务通过将其提交给相同的Executor来“分离”另一个任务。 然后给它选择稍后尝试“帮助加入”该任务,如果它尚未运行则尝试运行它。
我相信通过简单地将Executor
与FutureTask
相结合,可以在Java 6中完成同样的事情。 像这样:
public class Fib implements Callable<Integer> {
int n;
Executor exec;
Fib(final int n, final Executor exec) {
this.n = n;
this.exec = exec;
}
/**
* {@inheritDoc}
*/
@Override
public Integer call() throws Exception {
if (n == 0 || n == 1) {
return n;
}
//Divide the problem
final Fib n1 = new Fib(n - 1, exec);
final Fib n2 = new Fib(n - 2, exec);
//FutureTask only allows run to complete once
final FutureTask<Integer> n2Task = new FutureTask<Integer>(n2);
//Ask the Executor for help
exec.execute(n2Task);
//Do half the work ourselves
final int partialResult = n1.call();
//Do the other half of the work if the Executor hasn't
n2Task.run();
//Return the combined result
return partialResult + n2Task.get();
}
}
您可以使用回调而不是让您的线程等待任务完成。 您的任务本身需要回调,因为他们提交了更多任务。
例如:
public class ParallelTask implements Runnable, Callback {
private final Callback mCB;
private final int mNumChildTasks;
private int mTimesCalledBack = 0;
private final Object mLock = new Object();
private boolean mCompleted = false;
public ParallelTask(Callback cb) {
mCB = cb;
mNumChildTasks = N; // the number of direct child tasks you know this task will spawn
// only going down 1 generation
// of course you could figure this number out in the run method (will need to be volatile if so)
// just as long as it is set before submitting any child tasks for execution
}
@Override
public void run() {
// do your stuff
// and submit your child tasks, but don't wait on them to complete
synchronized(mLock) {
mCompleted = true;
if (mNumChildTasks == mTimesCalledBack) {
mCB.taskCompleted();
}
}
}
// Callback interface
// taskCompleted is being called from the threads that this task's children are running in
@Override
public void taskCompleted() {
synchronized(mLock) {
mTimesCalledBack++;
// only call our parent back if our direct children have all called us back
// and our own task is done
if (mCompleted && mTimesCalledBack == mNumChildTasks) {
mCB.taskCompleted();
}
}
}
}
在主线程中,您提交根任务并注册一些要执行的回调。
由于所有子任务在子项报告完成之前都不会报告完成,因此在完成所有操作之前不应调用根回调。
我在运行中写了这个并没有测试或编译它,所以可能会有一些错误。
似乎问题是任务也试图并行化自己,这使得难以避免资源限制。 你为什么需要这样做? 为什么不总是内联运行子任务?
如果您已经通过并行化充分利用了cpu,那么通过将工作再次划分为更小的任务,您将不会在完成整体工作方面购买太多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.