简体   繁体   English

ExecutorService - 使用特定时间限制执行每个任务

[英]ExecutorService - Execute Each Task with a Specific Time Limit

I'm creating an ExecutorService to execute some tasks which under normal conditions is expected to take one minute to complete but under no circumstances should be allowed to run for more than two minutes from when the task started . 我正在创建一个ExecutorService来执行一些任务,这些任务在正常情况下预计需要一分钟才能完成,但在任何情况下都不允许从任务开始后运行超过两分钟。

My code is as follows: 我的代码如下:

ExecutorService executorService = Executors.newFixedThreadPool(10);
ArrayList<Future<?>> futuresList = new ArrayList<Future<?>>();



for (String singleTask: taskList) { 
                futuresList.add(executorService.submit( new Runnable(){      
                       @Override
                       public void run(){
                        try {
                            performTask(p1, singleTask, p3);
                        } catch (IOException | InterruptedException | ExecutionException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                }
              }
         }));       

    }



for(Future<?> future : futures) {
    future.get(120, TimeUnit.SECONDS); 
}

This will block until specified timeout and then move on. 这将阻止直到指定的超时,然后继续。 My problem(s) are as follows: 我的问题如下:

1) If task1 blocks for two minutes and task2 also blocks for two minutes - then task2 will have "blocked" for a total of 4 minutes (since future.get(120, TimeUnit.SECONDS); is not called on task2 until task1 finishes blocking) - even though both tasks were submitted and started executing at the same time 1)如果task1阻塞了两分钟而task2也阻塞了两分钟 - 那么task2将被“阻塞”总共4分钟(因为future.get(120, TimeUnit.SECONDS);task1完成之前不会在task2上调用阻止) - 即使两个任务都已提交并同时开始执行

2) If I submit more than 10 tasks, task 11+ may never block for the desired amount of time, if previous tasks have not complete by the time future.get(120, TimeUnit.SECONDS); 2)如果我提交了10个以上的任务,那么任务11+可能永远不会阻塞所需的时间, 如果以前的任务还没有完成,那么将在future.get(120, TimeUnit.SECONDS);完成。 future.get(120, TimeUnit.SECONDS); is called on the 11th task 被称为第11任务

My goal is to have each individual task execute for a max of two minutes, regardless of the number of tasks in list and regardless of how many tasks come before it or after it. 我的目标是让每个单独的任务执行最多两分钟,无论列表中的任务数量多少,无论在它之前或之后有多少任务。

Thanks 谢谢

You can take the time with System.currentTimeMillis(); 你可以花时间使用System.currentTimeMillis(); then you add the maximum time, eg 120_000 milliseconds. 然后添加最长时间,例如120_000毫秒。 When you wait you subtract the current time. 等你的时候减去当前时间。 Ie you only wait until the maximum time is reached. 即你只等到达到最大时间。

ExecutorService#invokeAll could be the key here. ExecutorService#invokeAll可能是这里的关键。

The question is a bit hard to understand (maybe even because you tried to describe it precisely? ;-)). 问题有点难以理解(甚至可能因为你试图准确地描述它?;-))。 So I created an example, trying to wrap my head around it. 所以我创造了一个例子,试图绕过它。 Even if this is not what you intended, maybe you can explain in how far this differs from your goal, so that the question can be clarified or others may write a better answer. 即使这不是你想要的,也许你可以解释这与你的目标有多远,以便澄清问题或者其他人可以写出更好的答案。

The example creates the tasks, as Callable objects, that are put into a list. 该示例创建作为Callable对象的任务,这些任务被放入列表中。 Such a list can be passed to ExecutorService#invokeAll . 这样的列表可以传递给ExecutorService#invokeAll (In your case, you could probably create these instances from your Runnable tasks with Executors#callable ). (在您的情况下,您可以使用Executors#callableRunnable任务创建这些实例)。 There are 5 tasks created. 创建了5个任务。 Each task takes, by default, 2000ms to execute. 默认情况下,每个任务执行2000毫秒。 Task "C" is the odd one out, and takes 8000ms. 任务"C"是奇数输出,需要8000ms。 The maximum execution time should be 5000ms. 最长执行时间应为5000毫秒。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ExecutorServiceLimitTaskTime
{
    private static Map<String, Long> taskSubmitMs = 
        new ConcurrentHashMap<String, Long>();
    private static Map<String, Long> taskStartMs = 
        new ConcurrentHashMap<String, Long>();
    private static Map<String, Long> taskFinishedMs = 
        new ConcurrentHashMap<String, Long>();

    public static void main(String[] args) throws Exception
    {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        List<String> tasks = Arrays.asList("A", "B", "C", "D", "E");

        List<Callable<String>> callables = new ArrayList<Callable<String>>();
        for (String task : tasks)
        {
            taskSubmitMs.put(task, System.currentTimeMillis());

            callables.add(new Callable<String>()
            {
                @Override
                public String call()
                {
                    taskStartMs.put(task, System.currentTimeMillis());

                    long durationMs = 2000;
                    if (task.equals("C"))
                    {
                        durationMs = 8000;
                    }

                    performTask(task, durationMs);
                    if (!Thread.currentThread().isInterrupted())
                    {
                        taskFinishedMs.put(task, System.currentTimeMillis());
                    }
                    return task;
                }
            });
        }

        List<Future<String>> futures = 
            executorService.invokeAll(callables, 5000, TimeUnit.MILLISECONDS);

        for (Future<String> future : futures)
        {
            try
            {
                future.get();
            }
            catch (CancellationException e) 
            {
                System.out.println("One task was cancelled");
            }
        }

        for (String task : tasks)
        {
            Long submitMs = taskSubmitMs.get(task);
            Long startMs = taskStartMs.get(task);
            Long finishedMs = taskFinishedMs.get(task);

            if (finishedMs != null)
            {
                long waitMs = startMs - submitMs;
                long runMs = finishedMs - startMs;
                long totalMs = finishedMs - submitMs;
                System.out.printf(
                    "Task %-3s waited %5d ms and ran %5d ms, total %5d ms\n", 
                    task, waitMs, runMs, totalMs);
            }
            else
            {
                System.out.printf(
                    "Task %-3s was cancelled\n", task);

            }
        }

    }

    private static void performTask(String task, long durationMs)
    {
        System.out.println("Executing " + task);
        try
        {
            Thread.sleep(durationMs);
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
        }
        System.out.println("Executing " + task + " DONE");
    }

}

The summary that is printed at the end shows this result: 最后打印的摘要显示了以下结果:

Task A   waited    16 ms and ran  2002 ms, total  2018 ms
Task B   waited     3 ms and ran  2002 ms, total  2005 ms
Task C   was cancelled
Task D   waited  2005 ms and ran  2000 ms, total  4005 ms
Task E   waited  2005 ms and ran  2000 ms, total  4005 ms

This shows that 这表明了这一点

  • The tasks that started immediately ran for 2000ms 立即启动的任务运行了2000毫秒
  • The tasks that had to wait for others also ran for 2000ms (but 4000ms in total) 必须等待其他人的任务也运行了2000毫秒(但总共4000毫秒)
  • The task that took too long was cancelled after 5000ms 花了太长时间的任务在5000ms后取消了

OK, I'm not sure if my question was fully understood but I will attempt to answer my own question with the solution I came up with (this might help clarify the question to others). 好吧,我不确定我的问题是否完全被理解,但我会尝试用我提出的解决方案回答我自己的问题(这可能有助于向其他人澄清问题)。 I think @Peter Lawrey eluded to this answer but the answer was too short to know for sure. 我认为@Peter Lawrey没有回答这个问题,但答案太短暂,无法确定。

        int timeLimitOfIndividualTaskInSeconds = 120;
        int fixedThreadPoolCount = 10;

        ExecutorService executorService = Executors.newFixedThreadPool(fixedThreadPoolCount);
        ArrayList<Future<?>> futuresList = new ArrayList<Future<?>>();

        for (String singleTask: taskList) {

            futuresList.add(executorService.submit( new Runnable(){      
                @Override
                public void run(){
                    try {
                        executeTask(singleTask);
                    } catch (IOException | InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }));        

        }

        long beforeTimeInMilli = System.currentTimeMillis();
        long beforeTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(beforeTimeInMilli);
        int counter = 0;

        long timeoutInSeconds = timeLimitOfIndividualTaskInSeconds;

        for(Future<?> future : futuresList) {
            if (counter % fixedThreadPoolCount == 0) {

                // resets time limit to initial limit since next batch of tasks are beginning to execute
                timeoutInSeconds = timeLimitOfIndividualTaskInSeconds;
            }

            try {
                future.get(timeoutInSeconds, TimeUnit.SECONDS);
            } catch (Exception e){
                e.printStackTrace();
                future.cancel(true); //stops the underlying task
            }

            counter++;

            long afterTimeInMilli = System.currentTimeMillis();
            long afterTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(afterTimeInMilli);

            long taskDurationInSeconds = afterTimeInSeconds - beforeTimeInSeconds;
            timeoutInSeconds = timeoutInSeconds - taskDurationInSeconds;

        }   

This guarantees two things: 这保证了两件事:

1) All tasks that have been submitted and started executing at the same time (ie, "same batch") will run for a max of 120 seconds (but if any task completes before 120 seconds it will not continue block) 1)所有已经提交并同时开始执行的任务(即“同一批次”)将运行最多 120秒(但如果任何任务在120秒之前完成,则不会继续阻止)

2) Prior tasks in the same batch will not cause subsequent tasks in that batch to execute for longer than 120 second time limit (since we deduct execution time of previous tasks from timeout value in subsequent tasks) 2)同一批次中的先前任务不会导致该批次中的后续任务执行超过120秒的时间限制(因为我们从后续任务中的超时值中扣除先前任务的执行时间)

I find this simple and elegant solution - but of course I'm happy to hear from anyone who is able to enhance or comment on this solution. 我找到了这个简单而优雅的解决方案 - 当然,我很高兴听到任何能够增强或评论此解决方案的人。

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

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