使计算线程以智能方式取消

时间:2013-11-24 21:13:49

标签: java multithreading performance

我想知道如何在快速取消响应和性能之间达成妥协,我的线程看起来类似于这个循环:

for(int i=0; i<HUGE_NUMBER; ++i) {
    //some easy computation like adding numbers
    //which are result of previous iteration of this loop
}

如果循环体中的计算非常简单,那么在每次迭代中添加简单的检查反应:

if (Thread.currentThread().isInterrupted()) {
    throw new InterruptedException("Cancelled");            
}

可能减慢代码的执行。

即使我将上述条件更改为:

if (i % 100 && Thread.currentThread().isInterrupted()) {
    throw new InterruptedException("Cancelled");            
}

然后,编译器不能只预先计算i的值并仅在某些特定情况下检查条件,因为HUGE_NUMBER是可变的并且可以具有不同的值。

所以我想询问是否有任何智能方式将此类检查添加到显示的代码中,并知道:

  • HUGE_NUMBER是可变的,可以有不同的值
  • 循环体包含一些易于计算,但依赖于prevoius计算代码。

我想说的是循环的一次迭代非常快,但迭代的HUGE_NUMBER可能会花费更多的时间,这就是我想要避免的。

2 个答案:

答案 0 :(得分:2)

首先,在这种情况下使用Thread.interrupted()代替Thread.currentThread().isInterrupted()

你应该考虑一下检查中断标志是否真的会减慢你的计算速度!一方面,如果循环体非常简单,即使是大量的迭代(上限为Integer.MAX_VALUE)也会在几秒钟内运行。即使检查中断标志会导致20%或30%的开销,这也不会对算法的总运行时间产生太大影响。

另一方面,如果循环体不那么简单,那么它会运行得更长,测试中断标志不会是一个非常显着的开销。

不要像if (i % 10000 == 0)这样的技巧,因为这会比“短”Thread.interrupted()更慢地减慢计算速度。

您可以使用一个小技巧 - 但请三思,因为它会使您的代码更复杂,更不易读取:

每当你有这样的循环时:

for (int i = 0; i < max; i++) {
    // loop-body using i
}

您可以将i的总范围拆分为多个大小INTERVAL_SIZE的间隔:

int start = 0;
while (start < max) {       
    final int next = Math.min(start + INTERVAL_SIZE, max);
    for(int i = start; i < next; i++) {
        // loop-body using i
    }
    start = next;
}

现在您可以在内循环之前或之后添加中断检查!


我使用以下loop-body

对我的系统(JDK 7)进行了一些测试
if (i % 2 == 0) x++;

Integer.MAX_VALUE / 2次迭代。结果如下(预热后):

  • 没有任何中断检查的简单循环: 1,949 ms
  • 每次迭代检查的简单循环: 2,219 ms (+ 14%)
  • 使用模数进行每100万次迭代检查的简单循环: 3,166 ms (+ 62%)
  • 使用位掩码每100万次迭代检查的简单循环: 2,653 ms (+ 36%)
  • 如上所述的区间循环,带有外部循环检查: 1,972 ms (+1.1%)

因此,即使循环体如上所述,每次迭代检查的开销仅为14%!所以建议不要做任何技巧,但只需在每次迭代中通过Thread.interrupted()检查中断标志!

答案 1 :(得分:1)

将您的计算设为Iterator

虽然这听起来不是非常有用,但是这里的好处是你可以很容易地编写过滤器迭代器,它可以非常灵活。它们可以简单地添加和删除 - 如果您愿意,甚至可以进行配置。有很多好处 - 试试吧。

然后,您可以添加一个过滤Iterator来监视时间并定期检查中断 - 或者更灵活的事情。

您甚至可以通过在脆弱的状态检查中穿插来添加进一步的过滤,而不会影响原始计算。