繁体   English   中英

使用 Callable 和 ExecutorCompletionService,future.cancel() 不起作用

[英]Using Callable and ExecutorCompletionService, future.cancel() does not work

我正在使用一堆可调用对象来搜索单个块中的列表,一旦返回 true,我想取消所有其他正在运行的可调用对象。 future.cancel 不会取消它们

我的号码查找器

    public class NumberFinderImpl implements NumberFinder {
  // how many threads the exucutor will use to start the callables
  private final int NUMBER_THREADS = 20;
  // the amount of elements in the array we will search in each callable chunk
  private final int CHUNK_ARRAY_SIZE = 5;

  @Override
  public boolean contains(int valueToFind, List<CustomNumberEntity> arrayToSearch) {
    long startTime = System.nanoTime();
    ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(NUMBER_THREADS);
    CompletionService<Boolean> completionService =
        new ExecutorCompletionService<>(WORKER_THREAD_POOL);
    int numberOfChunksNeeded = (int) Math.ceil(arrayToSearch.size() / CHUNK_ARRAY_SIZE);
    // get a callable for each chunk search
    List<Callable<Boolean>> callablesForEachChunkSearch =
        getCallablesForEachChunk(
            CHUNK_ARRAY_SIZE, numberOfChunksNeeded, valueToFind, arrayToSearch);
    // start the callables and collect the futures
    List<Future<Boolean>> futuresForCallables =
        callablesForEachChunkSearch
            .stream()
            .map(completionService::submit)
            .collect(Collectors.toList());
    for (int j = 0; j < futuresForCallables.size(); j++) {
      try {
        // take().get() is blocking
        // so if a callable is not done yet
        // it will wait until it is before proceeding
        Boolean chunkResult = completionService.take().get();
        if (chunkResult) {
          long endTime = System.nanoTime();
          long timeTaken = endTime - startTime;
          // TimeUnit
          long timeInSeconds = TimeUnit.SECONDS.convert(timeTaken, TimeUnit.NANOSECONDS);
          System.out.println("Search time in seconds" + timeInSeconds);
          for (Future<Boolean> future : futuresForCallables) {
            // cancel all the other running callables
            future.cancel(true);
          }
          return true;
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (ExecutionException e) {
        e.printStackTrace();
      }
    }
    for (Future<Boolean> future : futuresForCallables) {
      // cancel all the other running callables
      future.cancel(true);
    }
    long endTime = System.nanoTime();
    long timeTaken = endTime - startTime;
    // TimeUnit
    long timeInSeconds = TimeUnit.SECONDS.convert(timeTaken, TimeUnit.NANOSECONDS);
    System.out.println("Search time in seconds" + timeInSeconds);
    return false;
  }

  // get a list of callables that each search a certain chunk of the array
  private List<Callable<Boolean>> getCallablesForEachChunk(
      int chunkArraySize,
      int numberOfChunksNeeded,
      int valueToFind,
      List<CustomNumberEntity> arrayToSearch) {
    List<Callable<Boolean>> callableList = new ArrayList<>(numberOfChunksNeeded);
    for (int i = 0; i < numberOfChunksNeeded; i++) {
      int startPosForCallable = 0;
      if (i > 0) {
        startPosForCallable = i * chunkArraySize;
      }
      // dont let end pos go out of bounds
      // if the chunk extends past the size, just set endPos as the end of the array
      int endPosForCallable =
          Math.min(startPosForCallable + chunkArraySize - 1, arrayToSearch.size());
      Callable<Boolean> callableSearch =
          new NumberFinderCallable(
              arrayToSearch, valueToFind, startPosForCallable, endPosForCallable);
      callableList.add(callableSearch);
    }
    return callableList;
  }

我的可调用对象进行搜索

    public class NumberFinderCallable implements Callable<Boolean> {

  private List<CustomNumberEntity> arrayToSearch;
  private int startPos;
  private int endPos;
  private int valueToSearchFor;

  public NumberFinderCallable(
      List<CustomNumberEntity> arrayToSearch, int valueToSearchFor, int startPos, int endPos) {
    this.arrayToSearch = arrayToSearch;
    this.startPos = startPos;
    this.endPos = endPos;
    this.valueToSearchFor = valueToSearchFor;
  }

  @Override
  public Boolean call() {
    System.out.println(
        "Callable started, searching the chunk of array with start pos "
            + startPos
            + " and end pos "
            + endPos);
    for (int i = startPos; i <= endPos; i++) {
      System.out.println(
          "Callable is comparing a number in pos "
              + i
              + " in the chunk with star pos "
              + +startPos
              + " and end pos "
              + endPos);
      if (FastestComparator.compare(valueToSearchFor, arrayToSearch.get(i)) == 0) {
        System.out.println("element found in pos " + i + ". Returning true");
        return true;
      }
    }
    return false;
  }
}

即使在找到真实结果并且所有期货都被取消后,我也可以从日志中看到线程仍在运行

根据您之前的问题,您已经明确表示,由于性能原因,您正在尝试并行化此contains方法,而不是为了了解 API。 但是,IMO 您错误地认为该方法实际上需要优化。

我对包含100000000 (1 亿)个元素的列表进行了临时测试,并针对最坏的情况,即尝试查找列表中不存在的元素。 对于顺序contain方法

list.contains(Integer.MAX_VALUE)

平均大约需要:

0.25 seconds

以及使用流的并行版本:

list.parallelStream().anyMatch(i -> i.equals(Integer.MAX_VALUE))

平均大约需要:

 0.19 seconds

在 4 核机器中加速1.32x 我非常怀疑一个人会取得比这更多的成就。 更不用说以下的可维护性和可读性:

list.parallelStream().anyMatch(i -> i.equals(Integer.MAX_VALUE))

与使用显式执行器等的潜在并行解决方案相比。

如果contain方法在性能方面非常重要,并且列表中没有重复的元素,则应该考虑使用HashSet来获得恒定复杂度时间的contain方法。

而不是ExecutorService

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(NUMBER_THREADS);

使用ThreadPoolExecutor

ThreadPoolExecutor WORKER_THREAD_POOL = (ThreadPoolExecutor) Executors.newFixedThreadPool(NUMBER_THREADS);

然后执行purge()以释放已取消期货的线程,例如

future.cancel(true);
WORKER_THREAD_POOL.purge();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM