Implementierung verschiedener Task-Typen

Die Tasks erfüllen verschiedene Aufgaben und können vom TaskRunner parallel ausgeführt werden. Ob ein so komplexes Task-Management notwendig ist, bleibt offen.
This commit is contained in:
ngb
2022-07-07 21:18:45 +02:00
parent 3030445dcf
commit 9ee7c606fe
6 changed files with 225 additions and 9 deletions

View File

@@ -0,0 +1,65 @@
package schule.ngb.zm.tasks;
import schule.ngb.zm.Zeichenmaschine;
import java.util.concurrent.Delayed;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
public abstract class DelayedTask extends Task implements Delayed {
protected long startTime = System.currentTimeMillis(); // in ms
/**
* Gibt die absolute Verzögerung der Task zurück. Im Gegensatz zu
* {@link #getDelay(TimeUnit)} sollte das Ergebnis von {@code getDelay()}
* bei mehrmaligem Aufruf konstant bleiben.
*
* @return Die ursprüngliche Verzögerung in Millisekunden
*/
public abstract int getDelay();
public long getStartTime() {
return startTime + getDelay();
}
/**
* Gibt die verbleibende Verzögerung bis zur Ausführung der Task zurück. Im
* Gegensatz zu {@link #getDelay()} sollte für mehrere Aufrufe von
* {@code getDelay(TimeUnit)} gelten, dass der zeitlich spätere Aufruf einen
* kleineren Wert zurückgibt, als der Frühere (abhängig von der gewählten
* {@link TimeUnit}).
*
* @param unit Die Zeiteinheit für die Verzögerung.
* @return Die verbleibende Verzögerung in der angegebenen Zeiteinheit.
*/
@Override
public long getDelay( TimeUnit unit ) {
int diff = (int) (getStartTime() - System.currentTimeMillis());
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo( Delayed o ) {
return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public void run() {
long delay = getDelay(TimeUnit.MILLISECONDS);
while( delay > 0 ) {
try {
wait(delay);
} catch( InterruptedException e ) {
// Keep waiting
}
delay = getDelay(TimeUnit.MILLISECONDS);
}
running = true;
this.update(0.0);
running = false;
done = true;
}
}

View File

@@ -0,0 +1,51 @@
package schule.ngb.zm.tasks;
import schule.ngb.zm.Constants;
import schule.ngb.zm.Zeichenmaschine;
public abstract class FrameSynchronizedTask extends Task {
private static Thread mainThread;
private static final Thread getMainThread() {
if( mainThread == null ) {
mainThread = Thread.currentThread();
if( !mainThread.getName().equals("Zeichenthread") ) {
// Need to search for main Zeichenthread ...
}
}
return mainThread;
}
@Override
public void run() {
running = true;
int lastTick = 0;
Thread lock = getMainThread();
while( running ) {
lastTick = Constants.tick;
this.update(lastTick);
synchronized( lock ) {
while( lastTick >= Constants.tick ) {
/*try {
lock.wait();
} catch( InterruptedException e ) {
// We got interrupted ...
}*/
Thread.yield();
}
}
}
running = false;
done = true;
}
@Override
public boolean isActive() {
return false;
}
}

View File

@@ -0,0 +1,11 @@
package schule.ngb.zm.tasks;
import schule.ngb.zm.Constants;
public abstract class FramerateLimitedTask extends RateLimitedTask {
public int getRate() {
return Constants.framesPerSecond;
}
}

View File

@@ -0,0 +1,53 @@
package schule.ngb.zm.tasks;
public abstract class RateLimitedTask extends Task {
public abstract int getRate();
@Override
public final void run() {
if( running || done ) {
return;
}
// current time in ns
long beforeTime = System.nanoTime();
// store for deltas
long overslept = 0L;
// delta in ms
double delta = 0;
running = true;
while( running ) {
// delta in seconds
delta = (System.nanoTime() - beforeTime) / 1000000000.0;
beforeTime = System.nanoTime();
this.update(delta);
// delta time in ns
long afterTime = System.nanoTime();
long dt = afterTime - beforeTime;
long sleep = 0;
if( getRate() > 0 ) {
sleep = ((1000000000L / getRate()) - dt) - overslept;
}
if( sleep > 0 ) {
try {
Thread.sleep(sleep / 1000000L, (int) (sleep % 1000000L));
} catch( InterruptedException e ) {
// Interrupt not relevant
}
} else {
Thread.yield();
}
// Did we sleep to long?
overslept = (System.nanoTime() - afterTime) - sleep;
}
running = false;
done = true;
}
}

View File

@@ -0,0 +1,24 @@
package schule.ngb.zm.tasks;
import schule.ngb.zm.Updatable;
public abstract class Task implements Runnable, Updatable {
protected boolean running = false;
protected boolean done = false;
@Override
public boolean isActive() {
return running;
}
public boolean isDone() {
return !running & done;
}
public void stop() {
running = false;
}
}

View File

@@ -25,6 +25,11 @@ public class TaskRunner {
return runner;
}
public static Future<?> run( Task task ) {
TaskRunner r = getTaskRunner();
return r.pool.submit(task);
}
public static Future<?> run( Runnable task ) {
TaskRunner r = getTaskRunner();
return r.pool.submit(task);
@@ -35,18 +40,13 @@ public class TaskRunner {
return r.pool.submit(task, result);
}
public static Future<?> schedule( Runnable task, int ms ) {
TaskRunner r = getTaskRunner();
return r.pool.schedule(task, ms, TimeUnit.MILLISECONDS);
}
public static void invokeLater( Runnable task ) {
SwingUtilities.invokeLater(task);
}
public static void shutdown() {
if( runner != null ) {
runner.pool.shutdown();
/*runner.pool.shutdown();
try {
runner.pool.awaitTermination(SHUTDOWN_TIME, TimeUnit.MILLISECONDS);
} catch( InterruptedException ex ) {
@@ -55,15 +55,27 @@ public class TaskRunner {
if( !runner.pool.isTerminated() ) {
runner.pool.shutdownNow();
}
}
}*/
runner.pool.shutdownNow();
}
}
ScheduledExecutorService pool;
ExecutorService pool;
private TaskRunner() {
//pool = new ScheduledThreadPoolExecutor(4);
pool = Executors.newScheduledThreadPool(POOL_SIZE, new ThreadFactory() {
/*pool = Executors.newScheduledThreadPool(POOL_SIZE, new ThreadFactory() {
private final ThreadFactory threadFactory = Executors.defaultThreadFactory();
@Override
public Thread newThread( Runnable r ) {
Thread t = threadFactory.newThread(r);
t.setName("TaskRunner-" + t.getName());
t.setDaemon(true);
return t;
}
});*/
pool = Executors.newCachedThreadPool(new ThreadFactory() {
private final ThreadFactory threadFactory = Executors.defaultThreadFactory();
@Override