#Run a task periodically.

1 messages · Page 1 of 1 (latest)

balmy lava
#

Hello,
Few weeks ago I asked for help with my problem. My problem was mainly occurred because of the way I was running a task periodically.
I was running the task likes this:

ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1,Thread.ofVirtual().factory());
threadPoolExecutor.execute(this::task());

public void task(){

  long startTime = System.currentTimeMillis();
  try{

      //some code
  
  }catch(Exception ignored){
  }finally{

      long endTime = System.currentTimeMillis();

      //start a new task every 1000ms.
      //startIn = 1000ms - execution_time
      //if the startIn is negative. start the task immediately.
      long startIn = 1000 - (endTime - startTime);

      threadPoolExecutor.schedule(
        this::task, //runnable implementation
        startIn,
        TimeUnit.MILLISECONDS
      );

  }

}

The idea of my scheduling is to start a new task every 1000ms.
To do this, I take in consideration the execution time and I calculate the remained time. But, if the remained time is negative, start the next task immediately.

This code initially worked. But after a while, it suddenly stopped (without any error) and not schedule anymore.

I am asking for your help to find a good solution for this case.

I also don't want the tasks to be queued, like with scheduleAtFixedRate(). Because there is some case where I end up with multiple tasks in queue, and then all these tasks are executed one after another (e.g. 3 tasks in less than 1000ms).
I also don't like scheduleWithFixedDelay because is not taking in consideration the execution time, and I run the next tasks later than I want.

Continuation in comments

dark marshBOT
#

<@&987246841693360200> please have a look, thanks.

balmy lava
#

Now, going back to my initial question.
Based on the answers to my question I managed to write this:

threadPoolExecutor.schedule(
  this::execute
  0,
  TimeUnit.MILLISECONDS
);

protected void execute(){

  threadPoolExecutor.execute(() -> {

        long startTime = System.currentTimeMillis();

        try{
          task();
        }catch(Exception e){}

        long endTime = System.currentTimeMillis() - start;
        long startIn = 1000 - duration;

    try{
        threadPoolExecutor.schedule(
        this::execute
        startIn ,
        TimeUnit.MILLISECONDS
            );
    }catch(Exception e){
        Logger.info("Failed to schedule");
        e.printStackTrace();
    }

  });

}

Which seems to work without any problem (for now).
But for me, this solution doesn't look very nice.

So, I created this question in the idea that maybe you can help me to find a nice/better solution.

Thank you.

balmy lava
torpid flower
#

Why do you need that?

balmy lava
# torpid flower Why do you need that?

I want to run a new task at an exact period, but I don't want to queue tasks like scheduledAtFixedRate is doing. Instead I want to skip the execution, until the current task is done. If the execution time of the task is extending this period.

For example I start a task at the moment 0 with frequency 100ms.
If the execution time of the task is taking 350ms. The scheduledAtFixedRate, is creating new tasks at the moments 100, 200 and 300 and queue them. And at the moment 350, when the first task is done. All these 3 queued tasks are running one after eachother without any break.
In my use-case, I want the tasks from moments 100, 200 and 300 to be skipped and the next task to start at 400.

hollow delta
#

u should approach it using sxheduleAtFixedRate nonetheless

#

and simply add one layer of indirection

#

as in, the scheduler will be ur "clock"

#

and then u maintain the CompletableFuture of the current task

#

check if its done and if not, return without starting a new task

#

if done, start the actual task

#

and update the future

balmy lava
hollow delta
#

service.scheduleAtFixedRate(this::queueJob,...)
and then in queueJob u can do sth like

if (!task.isDone()) {
  return;
}
task = service.submit(this::runJob);
#

runJob is then the actual method u want to run

#

now runJob is only executed on the fixed schedule and skipped if its still busy

balmy lava
#

Thank you.