2017-03-20 2 views
0

J'essaie de planifier une tâche qui nécessite ~ 2,25 sec à exécuter toutes les secondes. Ainsi, je sais que 3 threads devraient suffire à gérer la charge. Mon code ressemble à ceci:Comment planifier une tâche à taux fixe avec une durée plus longue que le taux?

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

Fait intéressant, cette scheduledAtFixedDelay se comporte comme avec un retard de 0 (fils terminer tous les ~ 2,25 s).

Je sais que scheduleAtFixedRate peut être en retard si un thread s'exécute en retard. Mais ne devrait pas donner à l'exécuteur un plus grand pool de threads pour résoudre ce problème?

Je pourrais facilement contourner le problème en codant 3 exécuteurs toutes les 3 secondes, mais cela rendrait l'administration inutilement difficile.

Y at-il une solution plus facile à ce problème?

Répondre

3

Vous pouvez utiliser Threadpool et programmateur pour obtenir cet effet:

  1. Créer un fixedThreadPool (ou autre qui comblera vos besoins), laissez-dire avec 4 fils - le nombre de threads doit être basé le temps que chaque tâche consomme à exécuter et la vitesse à laquelle les tâches sont planifiées, essentiellement numberOfThreads = avgExecTimeOfSingleTaks * frequency + someMargin;

  2. puis de créer un thread de planificateur, que chaque seconde (ou toute autre période souhaitée) ajoute un travail à la file d'attente fixedThreadPool, les tâches peuvent se chevaucher.

Cette façon de résoudre le problème a adventage supplémentaire:

Si vos tâches commencent à prendre plus de 2,25 sec, ou si vous souhaitez les exécuter avec une plus grande fréquence, tout ce que vous devez faire est d'ajouter des threads au threadPool, alors qu'en utilisant l'autre réponse, vous devez recalculer et replanifier tout. Donc, cette approche vous donne une approche plus claire et plus facile à la maintenance.

+0

Aussi une solution valide. Attention, si vous avez un temps d'exécution> 3 secondes ici (un coup d'œil singulier peut-être) vous pouvez vous retrouver avec plus de 3 Tâches simultanées (étant donné qu'il y a plus de 3 Threads dans le pool disponible). – Fildor

+0

Pro Tipp: Incorporez certaines mesures et consignation de celles-ci pour attirer l'attention lorsque les tâches commencent à s'exécuter plus longtemps que prévu/souhaité. – Fildor

1

scheduledExecutor empêche automatiquement le chevauchement des exécutions de tâches. Ainsi, vos exécutions ultérieures seront retardées jusqu'à la fin de la précédente.

Docs:

« Si l'exécution de cette tâche est plus longue que la période, puis les exécutions suivantes peuvent commencer à la fin, mais ne sera pas exécuter simultanément. »

Alors vous avez besoin pour planifier Tâches avec 1) InitDelay 0 secs, 2) InitDelay 1 secs, 3) InitDelay 2 secs et pour chacun d'eux une période de 3 secondes.

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); 

Remarquez que l'exécution avec la ressource partagée peut entraîner une durée d'exécution plus longue, s'il existe de grandes portions de code synchronisé.

+0

que de vous. J'ai supposé autant des données, mais j'espérais une solution plus élégante. –

+0

Si le temps d'exécution de la tâche unique va augmenter, ou la vitesse à laquelle ils doivent être exécutés va croître? Qu'est-ce qu'il doit faire? Recalculer, replanifier et réécrire ce code? –

+0

@KrzysztofCichocki J'ai répondu en fonction des exigences de l'OP. Si ceux-ci changent, le code ci-dessus devra être adapté en conséquence, oui. C'est pourquoi j'ai donné votre solution +1 mais vous devez prendre des précautions et réfléchir à ce que devrait être le comportement souhaité. – Fildor