我有一个ThreadPoolExecutor - corePoolSize = 5,maxPoolSize = 10 queueSize = 10,keepAlive = 1000秒。我正在执行int i1 = xml.IndexOf("<LabelImage>") + "<LabelImage>".Length;
int i2 = xml.IndexOf("</LabelImage>");
Byte[] vbf = Convert.FromBase64String(xml.Substring(i1,i2-i1));
个任务。实际执行的任务数量是变化的,并非所有任务都被执行。 RejectionHandler也没有报告任何内容。我相信我对ThreadPoolExecutor的理解是错误的。有谁能够帮我?如何执行所有任务?
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xml);
var imageBase64 = xmldoc.GetElementsByTagName("LabelImage").Item(0).InnerText;
Byte[] image= Convert.FromBase64String(imageBase64);
示例输出:
100 Runnable
答案 0 :(得分:3)
假设100个线程不能被处理为maxPoolSize = 10和queueSize = 10,这意味着你可以在最坏的情况下放入你的拉执行器只有20个线程。最佳情况可能会根据每个线程内的作业的性能和复杂性而改变。尝试将queueSize增加到90.所以肯定有90个会等待,其他10个将继续工作。 您可以在link找到最佳的表达方式:
如果请求无法排队,则会创建一个新线程,除非这样 会超过maximumPoolSize,在这种情况下,任务将是 拒绝。
答案 1 :(得分:3)
我很欣赏 @Speise 提供的答案,但只是为了增加一些清晰而明显的细节;我会引用oracle文档中提到的声明:
如果正在运行少于corePoolSize的线程,则Executor总是更喜欢添加新线程而不是排队。
如果corePoolSize或更多线程正在运行,则Executor总是更喜欢排队请求而不是添加新线程。
如果请求无法排队,则会创建一个新线程,除非这会超过maximumPoolSize,在这种情况下,该任务将被拒绝。
文档非常清晰,并指出为了避免拒绝任务,您应该增加maxPoolSize。而不是增加Queue
大小,因为可能没有案例(如当你事先知道要提交多少任务时(你可以调整maxPoolSize)。我还建议你在这种类型的场景中使用UnboundedQueues
(LinkedBlockingQueue)(但是,对于这种情况可能有一个快速修复,你也可以增加将请求推送到队列的时间)。
同样增加队列大小会妨碍性能,所以为什么不将它(相应地创建线程)留给JVM。(因为ThreadPool
根据maxPoolSize
采取行动)
答案 2 :(得分:2)
TestThreadPoolExecutor的参数:
corePoolSize - 池中保留的线程数,即使它们处于空闲状态。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,这是多余空闲线程在终止之前等待新任务的最长时间。
unit - keepAliveTime参数的时间单位。
workQueue - 在执行任务之前用于保存任务的队列。此队列将仅保存execute方法提交的Runnable任务。
handler - 执行被阻止时使用的处理程序,因为已达到线程边界和队列容量。
您的情景: corePoolSize = 5,maximumPoolSize = 10,workqueue = 10
你正在推进100个任务for for循环。
首先,你永远不会有超过10个工作线程。
假设您的所有工作线程都忙,那么大多数工作队列可以保持10个。之后,任何更多的任务提交都将被拒绝,您可以在拒绝处理程序中捕获该拒绝。 因此,增加工作队列将有助于解决问题。 (但不推荐)
你可以尝试:
在推动工作时使用一些时间差距,而不是使用for循环,这实际上是在同一个实例中推动工作。
将maximumPoolSize设置为核心X 2(或4)的数量。
当您的工作被拒绝时,您可以尝试在延迟一段时间后重新提交工作
PS:这是生产者 - 消费者问题。它总是要求消费者比生产者更快。在某些时刻,生产者变得更快,我们看到工作负荷飙升。为了处理这种负载峰值,设计了队列,但即使队列溢出,拒绝也会被发送回生产者,生产者有责任通过重新提交或丢弃来优雅地处理它。
答案 3 :(得分:1)
感谢@ Mandy8055,@ Ritesh,@ Speise
如果无法预测queueSize,我找到了两种方法,可以优雅地处理这个问题。
编写拒绝处理程序,将任务提交回队列。但是,正如@Ritesh指出的那样,生产者可能比消费者更快,我们可能最终获得StackOverFlow。 无论如何,这就是我所做的:
class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
DummyRunnableTask task = (DummyRunnableTask)r;
System.out.println("Task id "+task.getI()+" got rejected, resubmitting");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.execute(r);
}
}
现在,这并不是处理它的好方法,因为你只是不知道,如果任务需要100毫秒才能完成。也许我们可以创建一个被拒绝的任务列表,然后重新提交它们。
另一种方法是在达到某个限制(maxPoolSize + queueSize)时阻止提交/执行方法ThreadPoolExecutor并使其等待直到有空格。我们通过在ThreadPoolExecutor的自定义实现中使用Bounded Semaphore来实现这一点
class CustomExecutor {
private final ThreadPoolExecutor executor;
private final Semaphore semaphore;
public CustomExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, TimeUnit unit,int queueSize, ThreadFactory factory) {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(queueSize);
this.executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, queue, factory);
this.semaphore = new Semaphore(queueSize + maxPoolSize);
}
public void shutDown(){
this.executor.shutdown();
}
public ThreadPoolExecutor getExecutor() {
return executor;
}
private void exec(final Runnable command) throws InterruptedException {
semaphore.acquire();
try {
executor.execute(new Runnable() {
@Override
public void run() {
try {
command.run();
} finally {
semaphore.release();
}
}
});
} catch (RejectedExecutionException e) {
semaphore.release();
throw e;
}
}
public void execute (Runnable command) throws InterruptedException {
exec(command);
}
}
如果您能想到其他任何方式,请告诉我们。