簡體   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