Как запланировать задачу с фиксированной скоростью с продолжительностью, превышающей скорость?

Я пытаюсь запланировать задачу, которая требует ~ 2,25 секунды для запуска каждую секунду. Таким образом, я знаю, что 3 потока должно быть достаточно для обработки нагрузки. Мой код выглядит так:

private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
scheduler.scheduleAtFixedRate(new Streamer(), 0, 1000, TimeUnit.MILLISECONDS);

интересно, это ведет себя как запланированноеAtFixedDelay с задержкой 0 (потоки заканчиваются каждые ~ 2,25 секунды).

Я знаю, что scheduleAtFixedRate может опаздывать, если поток опаздывает. Но разве предоставление исполнителю большего пула потоков не должно решить эту проблему?

Я мог бы легко обойти проблему, запрограммировав 3 исполнителя для запуска каждые 3 секунды, но это сделало бы администрирование излишне сложным.

Есть ли более простое решение этой проблемы?


person Anders Bernard    schedule 20.03.2017    source источник


Ответы (2)


Для достижения этого эффекта можно использовать threadPool и планировщик:

  1. Создайте FixedThreadPool (или другой, который удовлетворит ваши потребности), скажем, с 4 потоками — количество потоков должно основываться на времени, затрачиваемом на выполнение одной задачи, и скорости, с которой задачи планируются, в основном numberOfThreads = avgExecTimeOfSingleTaks * частота + некоторая маржа;

  2. затем создайте поток планировщика, который каждую секунду (или другой желаемый период) будет добавлять задание в очередь fixedThreadPool, тогда задачи могут перекрываться.

Этот способ решения проблемы имеет дополнительные преимущества:

Если ваши задачи начинают выполняться дольше 2,25 с или вы хотите выполнять их с большей частотой, то все, что вам нужно сделать, это добавить некоторые потоки в threadPool, тогда как, используя другой ответ, вам нужно все пересчитать и перепланировать . Таким образом, этот подход дает вам более четкий и простой подход к обслуживанию.

person Krzysztof Cichocki    schedule 20.03.2017
comment
Тоже верное решение. Имейте в виду, что если у вас есть время выполнения > 3 секунды (может быть, единственный взгляд), вы можете получить более 3 одновременных задач (учитывая, что в пуле доступно более 3 потоков). - person Fildor; 20.03.2017
comment
Профессиональный совет: включите некоторые измерения и их регистрацию, чтобы привлечь внимание, когда задачи начинают выполняться дольше, чем ожидалось/желалось. - person Fildor; 20.03.2017

ScheduleExecutor автоматически предотвращает перекрытие выполнения задач. Таким образом, ваши последующие выполнения будут отложены до тех пор, пока не завершится предыдущее.

Документы:

«Если какое-либо выполнение этой задачи занимает больше времени, чем ее период, то последующие выполнения могут начаться с опозданием, но не будут выполняться одновременно».

Таким образом, вам нужно запланировать 3 задачи с 1) InitDelay 0 секунд, 2) InitDelay 1 секунд, 3) InitDelay 2 секунды и для каждого из них Period 3 секунды.

private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
final Streamer sharedStreamer = new Streamer();
scheduler.scheduleAtFixedRate(sharedStreamer, 0, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(sharedStreamer, 1, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(sharedStreamer, 2, 3, TimeUnit.SECONDS);

Имейте в виду, что выполнение с общим ресурсом может привести к увеличению времени выполнения, если есть большие части синхронизированного кода.

person Fildor    schedule 20.03.2017
comment
благодарю вас. Я предполагал это из данных, но надеялся на более элегантное решение. - person Anders Bernard; 20.03.2017
comment
Будет ли расти время выполнения одиночных задач или будет расти скорость, с которой они должны выполняться? Что Он должен делать? Пересчитать, перепланировать и переписать этот код? - person Krzysztof Cichocki; 20.03.2017
comment
@KrzysztofCichocki Я ответил, исходя из требований ОП. Если они изменятся, приведенный выше код необходимо будет соответствующим образом адаптировать, да. Вот почему я дал вашему решению +1, но вы должны принять меры предосторожности и подумать о том, каким должно быть желаемое поведение. - person Fildor; 20.03.2017
comment
@KrzysztofCichocki Я тоже. Я просто тоже не люблю преждевременную оптимизацию. Это решение автоматически предотвратит одновременное выполнение более 3 задач с автоматическим наверстыванием, если одно выполнение займет немного больше времени. Если ожидается большое колебание времени выполнения или вы изменяете код, который должен выполняться (что обычно приводит к увеличению времени выполнения), вам в любом случае необходимо пересмотреть расписание. Если ожидается, что такие изменения будут происходить часто, то предпочтительнее решение, которое можно легко адаптировать к новым значениям без изменения какого-либо кода. Можно было бы даже придумать какой-нибудь самонастраивающийся механизм. - person Fildor; 20.03.2017
comment
Ваша аргументация очень убедительна, но неверна. - person Krzysztof Cichocki; 20.03.2017