【问题标题】:How to let Task cancel it's own future?如何让任务取消它自己的未来?
【发布时间】:2018-11-30 03:33:36
【问题描述】:

这是为了学习目的。

想象一下,我想计算素数并使用ThreadPoolExecutor 来做。 下面你可以看到我当前的实现,这有点傻。 我的结构:
我生成一定范围内的数字。
对于每个生成的数字,创建一个任务来检查给定的数字是否是素数。
如果是素数,则运算结果为数,否则为null
收集器遍历结果列表并检查是否有数字或null。如果是数字,则将该数字写入某个文件(此处:按位数排序)

我想做的是:如果要在任务中检查的数字不是素数,请从列表中删除我的未来/取消它。据我所知,只有 Executor 本身可以取消 Future。 我想要的是任务本身说“嘿,我知道我的结果对你没有用,所以请在遍历列表时忽略我”。 我不知道该怎么做。

我现在在做什么(相关部分):

 final List<Future<Long>> resultList = new ArrayList<>();
        final BlockingQueue<Runnable> workingQueue = new ArrayBlockingQueue<>(CAPACITY);
        final ExecutorService exec = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors() - 2,
                Runtime.getRuntime().availableProcessors() - 1,
                5, TimeUnit.SECONDS,
                workingQueue,
                new ThreadPoolExecutor.CallerRunsPolicy()
        );


        for (long i = GENERATEFROM; i <= GENERATETO; i++) {
            Future<Long> result = exec.submit(new Worker(i));
            resultList.add(result);
        }
        Collector collector = new Collector(resultList,GENERATETO);
        collector.start();
        exec.shutdown();

一个工人在那里执行一项任务(它是一个素数吗?)

public class Worker implements Callable<Long> {

    private long number;

    public Worker(long number) {
        this.number = number;
    }


    //checks whether an int is prime or not.
    boolean isPrime(long n) {
        //check if n is a multiple of 2
        if (n % 2 == 0) return false;
        //if not, then just check the odds
        for (long i = 3; i * i <= n; i += 2) {
            if (n % i == 0)
                return false;
        }
        return true;
    }


    @Override
    public Long call() throws Exception {

        if (isPrime(number)) {
            return number;
        }
        return null;


    }
}

为了完整起见,我的收藏家:

public class Collector {

    private List<Future<Long>> primeNumbers;
    private long maxNumberGenerated;
    private HashMap<Integer, PrintWriter> digitMap;
    private final long maxWaitTime;
    private final TimeUnit timeUnit;


    public Collector(List<Future<Long>> primeNumbers, long maxNumberGenerated) {
        this.primeNumbers = primeNumbers;
        this.maxNumberGenerated = maxNumberGenerated;
        this.digitMap = new HashMap<>();
        this.maxWaitTime = 1000;
        this.timeUnit = TimeUnit.MILLISECONDS;
    }


    public void start() {

        try {
            //create Files
            int filesToCreate = getDigits(maxNumberGenerated);
            for (int i = 1; i <= filesToCreate; i++) {
                File f = new File(System.getProperty("user.dir") + "/src/solutionWithExecutor/PrimeNumsWith_" + i +
                        "_Digits.txt");
                PrintWriter pw = new PrintWriter(f, "UTF-8");
                digitMap.put(i, pw);
            }


            for (Future<Long> future : primeNumbers) {
                Object possibleNumber = future.get();
                if (possibleNumber != null) {
                    long numberToTest = (long) possibleNumber;
                    int numOfDigits = getDigits(numberToTest);
                    PrintWriter correspondingFileWriter = digitMap.get(numOfDigits);
                    correspondingFileWriter.println(possibleNumber.toString());
                    correspondingFileWriter.flush();

                }
            }
            for (PrintWriter fw : digitMap.values()) {
                fw.close();
            }


        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    private int getDigits(long maxNumberGenerated) {

        return String.valueOf(maxNumberGenerated).length();
    }


}

【问题讨论】:

  • 不要使用List&lt;Future&lt;Long&gt;&gt;,而是使用Map&lt;Integer, Future&lt;Long&gt;&gt;。当您从提交的任务中获得 Future&lt;Long&gt; 时,将其添加到带有 i 创建的地图中。然后,当任务注意到它的i 不是素数时,将其从Map 中删除。不过,Questin 是:为什么您甚至需要删除这些 Futures?听起来像是您可以通过list.stream().filter(f -&gt; f.get() != null).collect(Collectors.asList()); 处理的任务
  • 仅供参考:future.get() 返回一个 Long,因此您无需转换为 possibleNumber。您正在执行possibleNumber.toString() 两次,一次在getDigits(...) 内,一次直接。注意不是FileWriter 的变量,如correspondingFileWriterfw。此外,我会按需创建您的PrintWriters,以便从地图中获取它们,如果它们为空,请将PrintWriterput(...) 创建到地图中。您上面的循环可能会使PrintWriters 未关闭,因为它们不在digitalMap 中。
  • 是的,谢谢你的投入。这是我的第一个概念,还没有重构——你说的大部分已经完成了。我唯一没有做的是按需创建 PrintWriter,我认为最好事先进行创建,以提高可读性和在实际操作过程中更快
  • @Turing85 我查看了您的个人资料,您似乎对低级执行了解很多。使用 != null 而不是询问数字是否为空,流是否更快?我认为在检查是否存在实际地址(或该地址处的值,不知道 null 是如何工作的)或者它可能更慢的意义上是相同的,因为它必须等到所有期货都完成
  • @InDaPond 让我们在chat 讨论这个问题。

标签: java multithreading concurrency threadpool threadpoolexecutor


【解决方案1】:

我想做的是:如果要在任务中检查的数字不是素数,请从列表中删除我的未来/取消它。据我所知,只有 Executor 本身可以取消 Future。

在我看来,这似乎是一种不必要的优化。 Future 在那里,以便任务可以返回一个值。一旦任务确定它不是素数并返回null,与Future 关联的程序的“成本”可以忽略不计。没有什么可以“取消”的。该任务已经完成,剩下的就是允许Future 传回null 或素数Long 的内存。

由于我们谈论的是学习,在许多情况下,程序员过分担心性能,我们经常花时间优化应用程序中真正不是问题的部分。如果我使用一些 JVM 监视器(可能是 jconsole)看到应用程序内存不足,那么我可能会担心Futuress 的列表,否则我会编写干净且易于维护的代码。

如果您真的担心Future,那么根本不要将它们保存在列表中,只需在素数检查任务和主线程之间共享BlockingQueue&lt;Long&gt;。主要检查作业将add(...) 加入队列,主线程将take()。您应该考虑将nulls 放在列表中,否则除非您计算结果,否则您不会知道主要任务是否已完成。你会想检查 X 个随机数,然后你会知道当 X 个结果(空或数字)从 BlockingQueue&lt;Long&gt; 获取时它就完成了。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 2021-09-06
    • 2011-07-18
    • 2022-10-26
    • 1970-01-01
    相关资源
    最近更新 更多