IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Cadencement précis d'une thread


Sujet :

Java

  1. #1
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut Cadencement précis d'une thread
    Bonjour,

    Je souhaite exécuter une opération à une cadence très précise et rapide. De l'ordre de 3ms. Quand je dis précis, je veux dire pas 3,1ms, pas 2,9, mais 3ms, car je dois m'aligner sur d'autre système.

    La manière dont je bosse (voir le code ci-dessous), c'est qu'avant l'exécution de mon code, je mesure le temps avec System.nanoTime(). Je le remesure après. Je déduis la différence de ma fréquence pour savoir combien de temps je vais devoir mettre ma thread en sommeil.
    Problème, je n'arrive pas à avoir un rapport 1:1 entre le moment réel de réexuction de ma thread et le moment souhaité. J'ai un écart (relativement constante) de 2,5%. En termes de ms, ce n'est pas énorme: ça fait 0,075ms.
    Mais ça introduit un déphasage croissant avec les messages extérieures sur lesquelles j'essaye de me synchroniser.

    Voici mon code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
        public void run() {
     
            // Tant que la Thread qui m'a démarrée est égale à la Thread voulue par Start() et Stop(), alors je continue.
            Thread me = Thread.currentThread();
     
            // Latence: initialition
            iterStartTime = 0;
     
            // Boucle de traitement qui tournera toutes les (RATE - Latence)  millisecondes
            while (ProcessingThread == me) {
     
                // Latence: initialition du calcul du temps mis par m'exécution
                iterStartTime = System.nanoTime();
     
                // Exécution du travail
                clEvent[] midiouts = Process(eventsStack, processFrom, processFrom);
    	    ...	
     
                // On calcule le temps qu'on a pris pour cette exécution
                long iterLength = System.nanoTime() - iterStartTime;
     
     
                // on attend RATE (en microseconde)  - le temps qu'il a fallu pour processer (en nanosecondes)
                long r = RATE * 1000 - iterLength;
                if (r < 0) r = 0;
                long Rmilli = r / 1000000;
                int Rnano = (int) (r % 1000000);
     
                // On initilise pour la prochaine fois
                processFrom += RATE;
     
                try {
                    Thread.sleep(Rmilli, Rnano);
                } catch (InterruptedException ex1) {
                    ATVSTECentral.logger().log1.println("A problem occured while the thread is waiting:\n" + ex1.toString());
                }
            }
        }
    Est-ce que quelqu'un y voir une raison de ce déphasage ?
    Merci,

    Laurent

  2. #2
    Membre actif
    Inscrit en
    Février 2008
    Messages
    88
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 88
    Par défaut
    Il y a une possibilité que ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // on attend RATE (en microseconde)  - le temps qu'il a fallu pour processer (en nanosecondes)
                long r = RATE * 1000 - iterLength;
                if (r < 0) r = 0;
                long Rmilli = r / 1000000;
                int Rnano = (int) (r % 1000000);
     
                // On initilise pour la prochaine fois
                processFrom += RATE;
    prenne du temps non ?
    Mais je suis pas expert.

    alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // on attend RATE (en microseconde)  - le temps qu'il a fallu pour processer (en nanosecondes)
                iterStartTime2 = System.nanoTime();
                long r = RATE * 1000 - iterLength;
                if (r < 0) r = 0;
                long Rmilli = r / 1000000;
                int Rnano = (int) (r % 1000000);
     
    // On initilise pour la prochaine fois
                processFrom += RATE;
     
                Rnano+=-iterStartTime2+System.nanoTime();

    Je pense cependant qu'il faille faire autrement.
    En faite tu dois avoir plusieurs Thread de lecture qui se déclenche respectivement une tout de suite à 3ms à 6ms à 9ms. et ainsi de suite.
    Tu dois avoir une boucle de cadencement de ces threads de lecture qui permet de connaitre excatement quand les déclenchés soit lancement du premier Thread + 3ms.... Disons que c'est une solution à étudier.

    Il est certain que ton problème n'est pas évident. Il y a un tutoriel disponible sur ce site sur le temps réelle. Peut etre que cela peut etre utile

  3. #3
    Membre Expert
    Avatar de CheryBen
    Inscrit en
    Mai 2005
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Âge : 43

    Informations forums :
    Inscription : Mai 2005
    Messages : 1 599
    Par défaut
    L'idéal serait d'utiliser un Timer ainsi qu'une TimerTask. C'est l'API java qui gère le cadencement, par contre je ne connais pas sa précision à la nanoseconde près mais dans la théorie cela fonctionne. Voila comment les utiliser :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Test {
        public void methodA() {
            new Updater().start();
        }
    }
     
    public class Updater extends Timer{
        public static final int UPDATE_PERIODE = 3;
        public void start() {
            schedule(new TonTraitement(), 0, UPDATE_PERIODE);
        }
     
        private class TonTraitement extends TimerTask {
     
            public void run() {
                // ton traitement
            }
        }
    }

  4. #4
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut
    Merci. J'essaye dès que je rentre.

  5. #5
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut
    Bon je n'ai pas essayé le Timer, mais la doc indique un cadencement à la milliseconde, ce qui ne me convient pas. Il me faut un cadencement à la nanoseconde !!
    Je vais essayer la solution proposée par MrCba.

  6. #6
    Membre actif
    Inscrit en
    Février 2008
    Messages
    88
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 88
    Par défaut
    Citation Envoyé par lvr Voir le message
    Bon je n'ai pas essayé le Timer, mais la doc indique un cadencement à la milliseconde, ce qui ne me convient pas. Il me faut un cadencement à la nanoseconde !!
    Je vais essayer la solution proposée par MrCba.
    Mon idée s'approche beaucoup de celle du timer.
    Essaye de regarder les sources du timer, si cela se trouve il est facilement possible d'utiliser des Nanosecondes en l'adaptant.

  7. #7
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    Salut,

    je pense que la précision rechrchée est bien trop fine et que tu auras énormément de mal à implémenter ce genre de chose ailleurs que dans un système d'exploitation temps réel.

    Pour info, la granularité des timers pour Windows 2000 par exemple est de l'ordre de 10ms (15-16ms si je me souviens bien). On est à un facteur 100 de la précision que tu recherches.
    Et encore, là on parle d'une API native de l'OS. Toi visiblement tu veux implémenter ça en Java, sachant que la JVM n'est finalement qu'un processus parmi tant d'autre.

    La javadoc de System.currentTimeMillis() le dit d'ailleurs mieux que moi:
    Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.
    Et même si tu arrives finalement à contourner le problème, n'imorte quel OS standard pourra très bien "rendre la main" à un autre thread qui va consommer quelques millisecondes pile au moment où toi tu aurais voulu exécuter ton traitement.

    Il n'y a donc - amha - pas d'autre solution pour toi que l'OS temps-réel.

  8. #8
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut
    Pour info, la granularité des timers pour Windows 2000 par exemple est de l'ordre de 10ms (15-16ms si je me souviens bien). On est à un facteur 100 de la précision que tu recherches.
    Pas tout à fait d'accord. Sous WindowXP, l'erreur que j'ai ne se monte qu'à 3% c'est qui en soi n'est pas énorme. Et de manière constante. D'où mon idée que WindowsXP calcule tout ça avec la précision nécessaire mais qu'il me manque la prise en compte d'une partie de du processus lorsque j'essaye d'évaluer le temps qu'il prend. Et c'est dans cette direction qu'allait MrCba. Faillait voir l'erreur énorme que j'avais avant de passer à System.nanotIme() avec System.currentTimeMillis()

    Il n'y a donc - amha - pas d'autre solution pour toi que l'OS temps-réel
    Qu'est-ce que tu appelles l'OS temps-réel ?

  9. #9
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    Citation Envoyé par lvr Voir le message
    Pas tout à fait d'accord. Sous WindowXP, l'erreur que j'ai ne se monte qu'à 3%
    Tu as vraisemblablement essayé dans des conditions plus ou moins 'idéales'. Ressaye en faisant tourner d'autres processus derrière, qui font de petites choses de temps en temps. Ils prendront alors la main et vont sûrement te 'tuer' tes statistiques.
    Attention, l'idée des autres programmes qui tournent en arrière plan n'est qu'une exemple pour mettre en évidence le problème ; se dire que tu ne feras tourner *que* ton application ne résoudra pas le problème pour autant. En effet, tout OS a des dizaines de threads (démons sous linux, services sous windows) qui tourneront quoi qu'il arrive.

    Les OS dans leurs versions les plus courantes (Windows, MacOS, linux) ne sont pas destinés à faire du temps réel. Un OS récent 'classique' met en oeuvre le concept de 'preemptive multitasking' : en gros, chaque process prend successivement la main et a droit à maximum une période de temps appelée 'time slice' pendant laquelle il est le seul à s'exécuter. Ce time-slice avec le noyau windows NT (XP, ...) est typiquement de l'ordre de la dizaine de millisecondes (ce lien parle de 20ms par défaut pour linux).

    Concrètement, en admettant que le timeslice soit de 10ms, si un autre processus récupère la main et qu'il a de quoi bosser pendant 10ms, tu peux être sûr que ton application ne reprendra pas la main avant. Tu auras alors raté au minimum trois itérations de ton traitement.

    Je ne dis pas ici que tu ne pourras pas - in fine - t'approcher de la précision voulue mais que dans tous les cas rien ne te permettra jamais de garantir que tu tiendras ta précision quoi qu'il arrive (et donc même en prenant soin de ne lancer que ton application sur ta machine).

    Et si ton application doit par exemple recevoir des données ou récupérer une mesure en temps réel, tu peux être sûr que de temps en temps une donnée passera à la trappe. Après, à toi de voir si ça reste acceptable ou pas dans le cadre de ton projet.

    Qu'est-ce que tu appelles l'OS temps-réel ?
    Il y a des systèmes d'exploitation dédiés pour le temps réel qui permettent de garantir les ressources notamment en termes de temps alloué à chaque processus et les temps de réponse. RT Linux (patch du kernel linux standard) ou RTems (système complet) en sont deux exemples.

  10. #10
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    399
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 399
    Par défaut
    Oui ca me parait difficile d'avoir une précision a la microseconde et en Java encore plus qui rajoute son lot de processus pas vraiment controlé au dessus de l'oS (GC...)

    Sinon tu parle de déphasage. En fait tout dépend de la fonction exacte de ton appli mais si tu veux éviter une accumulation de l'erreur et donc un déphasage potentiellement croissant (disons que si la précision de l'instant auquel ta thread s'éxécute a plus d'importance que sa fréquence d'éxécution) plutot que de te baser sur la frame précédente pour calculer le sleep, tu peux te baser sur un temps initial.

    Par curiosité, pourquoi as tu besoin d'une telle précision et es tu bien sur que c'est nécéssaire ?
    SPARK
    Moteur de particule C++ opensource avec modules de rendu OpenGL, Irrlicht et SFML

  11. #11
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut
    Par curiosité, pourquoi as tu besoin d'une telle précision et es tu bien sur que c'est nécéssaire ?
    Je développe des applications audio.

    Pour celle sur laquelle je travaille maintenant, je ne fais que recevoir des messages extérieur ayant un timestamp précis auxquels je dois réagir au plus vite. Il faut que je reste aligné sinon je prends en compte leur messages trop tard. Mais je pense que je vais faire dans l'autre sens et déduire du temps le pas où je suis.

    Par contre avec de l'audio pure, travaillant à 96kHz, ça fait du 100ème de milliseconde ! Donc là il y a un problème si java ne permet ce séquencement précis. Va falloir ce que l'API Sound offre.

  12. #12
    lvr
    lvr est déconnecté
    Membre éclairé Avatar de lvr
    Profil pro
    Responsable de projet fonctionnel
    Inscrit en
    Avril 2006
    Messages
    919
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Responsable de projet fonctionnel
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2006
    Messages : 919
    Par défaut
    Frifron, tu avais raison, je n'avais pas réellement besoin de cette précision.
    Plutôt que d'essayer de cadencer parfaitement mon thread, c'est mon thread qui se synchronise sur l'horloge. Vu que la marge d'erreur était faible, ça a suffit à régler mon problème. Merci à tous pour vos commentaires.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 07/06/2007, 17h44
  2. Atteindre 1 enrg précis sur une fiche
    Par User dans le forum Bases de données
    Réponses: 5
    Dernier message: 11/03/2007, 16h24
  3. [C#] Comment une thread peut-elle attendre un evenement?
    Par legillou dans le forum Windows Forms
    Réponses: 4
    Dernier message: 03/07/2006, 15h58
  4. [MFC] CArchive dans une thread
    Par Kaori dans le forum MFC
    Réponses: 12
    Dernier message: 11/04/2005, 15h26
  5. Arrêter une Thread brutalement!
    Par Rodrigue dans le forum C++Builder
    Réponses: 2
    Dernier message: 18/01/2004, 21h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo