简体   繁体   English

等待守护程序线程使用执行程序服务完成迭代

[英]Wait for daemon threads to complete an iteration using an executor service

I have to parallelize an existing background task such that instead of serially consuming 'x' resources, it parallely finishes the job at hand using only 'y' threads (y << x). 我必须并行化一个现有的后台任务,这样它不必串行消耗“ x”资源,而是仅使用“ y”个线程(y << x)并行完成手头的工作。 This task constantly runs in the background and keeps processing some resources. 该任务始终在后台运行,并继续处理一些资源。

Code is structured as follows: 代码结构如下:

class BaseBackground implements Runnable {
    @Override
    public void run() {
        int[] resources = findResources(...);

        for (int resource : resources) {
            processResource(resource);
        }

        stopProcessing();
     }

    public abstract void processResource(final int resource);
    public void void stopProcessing() {
         // Override by subclass as needed
    }
}

class ChildBackground extends BaseBackground {

    @Override
    public abstract void processResource(final int resource) {
        // does some work here
    }

    public void void stopProcessing() {
        // reset some counts and emit metrics
    }
}

I've modified the ChildBackground in the following manner: 我以以下方式修改了ChildBackground

class ChildBackground extends BaseBackground {

    private final BlockingQueue<Integer> resourcesToBeProcessed;

    public ChildBackground() {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 2; ++i) {
             executorService.submit(new ResourceProcessor());
        }
    }

    @Override
    public abstract void processResource(final int resource) {
        resourcesToBeProcessed.add(resource);
    }

    public void void stopProcessing() {
        // reset some counts and emit metrics
    }

    public class ResourceProcessor implements Runnable {
        @Override
        public void run() {
            while (true) {
                int nextResource = resourcesToBeProcessed.take();
                // does some work
            }
        }
    }
}

I am not creating and tearing down ExecutorService each time because garbage collection is bit of a problem in my service. 我不会每次都创建和删除ExecutorService,因为垃圾回收在我的服务中有点问题。 Although, I do not understand how bad it'll be since I won't spawn more than 10 threads in every iteration. 虽然,我不知道会有多糟糕,因为每次迭代都不会产生超过10个线程。

I am not able to understand how do I wait for all the ResourceProcessor s to finish processing resources for one iteration so I can reset some counts and emit metrics in stopProcessing . 我无法理解如何等待所有ResourceProcessor完成一次迭代的ResourceProcessor处理,因此我可以重置一些计数并在stopProcessing发出指标。 I've considered the following options: 我考虑了以下选项:

1) executorService.awaitTermination(timeout). 1)executorService.awaitTermination(timeout)。 This won't really work as it will always block until the timeout because the ResourceProcessor threads will never really finish their jobs 这将不会真正起作用,因为它将一直阻塞直到超时,因为ResourceProcessor线程将永远无法真正完成其工作。

2) I can find out the number of resources after findResources and make it available to the child class and have each ResourceProcessor increment the number of resources processed. 2)我可以找出findResources之后的资源数量,并将其提供给子类,并让每个ResourceProcessor增加处理的资源数量。 I will have to wait for all the resources to be processed in stopProcessing before resetting counts. 在重置计数之前,我将不得不等待stopProcessing处理的所有资源。 I need something like CountDownLatch, but it should count UP instead. 我需要CountDownLatch之类的东西,但是它应该算上UP There'll be a lot of state management in this option, which I am not particularly fond of. 此选项中将有很多状态管理,我对此并不特别喜欢。

3) I could update the public abstract void processResource(final int resource) to include count of total resources and have the child process wait until all threads have processed total resources. 3)我可以更新public abstract void processResource(final int resource)以包括总资源的数量,并让子进程等待,直到所有线程都处理了总资源。 There'll be some state management in this case also, but it'll be limited to the child class. 在这种情况下,还将有一些状态管理,但仅限于子类。

In either of the 2 cases, I will to have to add wait() & notify() logic, but I am not confident about my approach. 在这两种情况中的任何一种情况下,我都必须添加wait()和notify()逻辑,但是我对我的方法没有信心。 This is what I've: 这就是我所做的:

class ChildBackground extends BaseBackground {

    private static final int UNSET_TOTAL_RESOURCES = -1;

    private final BlockingQueue<Integer> resourcesToBeProcessed;

    private int totalResources = UNSET_TOTAL_RESOURCES;
    private final AtomicInteger resourcesProcessed = new AtomicInteger(0);

    public ChildBackground() {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 2; ++i) {
             executorService.submit(new ResourceProcessor());
        }
    }

    @Override
    public abstract void processResource(final int resource, final int totalResources) {
        if (this.totalResources == UNSET_TOTAL_RESOURCES) {
            this.totalResources = totalResources;
        } else {
            Preconditions.checkState(this.totalResources == totalResources, "Consecutive poll requests are using different total resources count, previous=%s, new=%s", this.totalResources, totalResources);
        }
        resourcesToBeProcessed.add(resource);
    }

    public void void stopProcessing() {
        try {
            waitForAllResourcesToBeProcessed();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resourcesProcessed.set(0);
        totalResources = UNSET_TOTAL_RESOURCES;
        // reset some counts and emit metrics
    }

    private void incrementProcessedResources() {
        synchronized (resourcesProcessed) {
            resourcesProcessed.getAndIncrement();
            resourcesProcessed.notify();
        }
    }

    private void waitForAllResourcesToBeProcessed() throws InterruptedException {
        synchronized (resourcesProcessed) {
             while (resourcesProcessed.get() != totalResources) {
                resourcesProcessed.wait();
             }
        }
    }

    public class ResourceProcessor implements Runnable {
        @Override
        public void run() {
            while (true) {
                int nextResource = resourcesToBeProcessed.take();
                try {
                   // does some work
                } finally {
                   incrementProcessedResources();
                }
            }
        }
    }
}

I'm not sure if using AtomicInteger is the right way to do it, and if so, do I need to call wait() and notify(). 我不确定使用AtomicInteger是否是正确的方法,如果是,是否需要调用wait()和notify()。 If I am not using wait() and notify() I don't even have to execute everything in a synchronized block. 如果我没有使用wait()和notify(),我什至不必在同步块中执行所有操作。

Please let me know your thoughts about this approach, if I should simply create and shutdown ExecutorService for every iteration or is there a fourth approach which I should pursue. 如果我应该为每个迭代简单地创建并关闭ExecutorService,或者我应该采用第四种方法,请告诉我您对这种方法的想法。

Your code seems to be unnecessarily complex. 您的代码似乎不必要地复杂。 Why have your own queue when there is a queue already inside of the ExecutorService ? ExecutorService内部已经存在队列时,为什么还要拥有自己的队列? You are having to do a whole bunch of administration when I think that you can let the stock ExecutorService handle it for you. 当我认为您可以让股票ExecutorService替您处理时,您必须进行一整套管理。

I'd define your jobs as: 我将您的工作定义为:

public static class ResourceProcessor implements Runnable {
   private final int resource;
   public ResourceProcessor(int resource) {
      this.resource = resource;
   }
   public void run() {
      try {
         // does some work
      } finally {
         // if this is still necessary then you should use a `Future` instead
         incrementProcessedResources();
      }
   }
}

Then you could submit them like: 然后,您可以像这样提交它们:

ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < totalResources; ++i) {
     executorService.submit(new ResourceProcessor(i));
}
// shutdown the thread pool after the last submit
executorService.shutdown();

executorService.awaitTermination(timeout) . executorService.awaitTermination(timeout) This won't really work as it will always block until the timeout because the ResourceProcessor threads will never really finish their jobs 这将不会真正起作用,因为它将一直阻塞直到超时,因为ResourceProcessor线程将永远无法真正完成其工作。

This would now work. 现在可以使用了。

2) I can find out the number of resources [have finished]. 2)我可以找出资源的数量[已经完成]。

Do you still need this if you can call awaitTermination(...) ? 如果可以调用awaitTermination(...)您是否仍需要此功能?

3) I could update the public abstract void processResource(final int resource) to include count of total resources and have the child process wait until all threads have processed total resources... 3)我可以更新公共抽象void processResource(final int resource)以包括总资源的数量,并让子进程等待,直到所有线程都处理了总资源为止。

Same question. 同样的问题。 Is this needed? 需要这个吗?

If you actually need to know the list of processed requests then you could, like @ScaryWombat mentioned use Future<Integer> and Callable<Integer> or use a ExecutorCompletionService . 如果您实际上需要知道已处理请求的列表,则可以像提到的@ScaryWombat一样使用Future<Integer>Callable<Integer>或使用ExecutorCompletionService

Futures aren't an option because the executor threads run within a tight loop that stops only when the service is deactivated. 期货不是一种选择,因为执行程序线程在紧密的循环内运行,该循环仅在停用服务后才会停止。

Can you explain this more? 您能再解释一下吗?

Hope this helps. 希望这可以帮助。

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

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