7
votes

Renister asynchrone intelligent en Java

J'ai un cas d'utilisation provenant d'un problème d'interface graphique que je voudrais soumettre à votre sagacité.

cas d'utilisation

J'ai une interface graphique qui affiche un résultat de calcul en fonction de certains paramètres de l'utilisateur dans une interface graphique. Par exemple, lorsque l'utilisateur déplace un curseur, plusieurs événements sont tirés, qui déclenchent tous un nouveau calcul. Lorsque l'utilisateur ajuste la valeur du curseur de A à B, une dizaine d'événements sont tirés.

mais le calcul peut prendre jusqu'à plusieurs secondes, alors que le réglage du curseur peut déclencher un événement tous les uns de 100 ms. < / P>

Comment écrire un thread approprié qui écoute ces événements et les filtreraient de manière à ce que la repeinte des résultats soit vif? Idéalement, vous voudriez quelque chose comme

  • Démarrez un nouveau calcul dès que le premier changement de changement est reçu;
  • Annulez le premier calcul si un nouvel événement est reçu et en commençant une nouvelle avec les nouveaux paramètres;
  • Mais assurez-vous que le dernier événement ne sera pas perdu, car le dernier calcul terminé doit être celui avec les derniers paramètres mis à jour.

    ce que j'ai essayé

    Un de mes amis (A. Cardona) a proposé cette approche de faible niveau d'un thread de mise à jour qui empêche trop d'événements de déclencher un calcul. Je copier-la coller ici (gpl):

    Il la met dans une classe qui étend le thread: xxx

    chaque fois qu'un événement est envoyé par L'interface graphique indiquant que les paramètres ont été modifiés, nous appelons updater.doupdate () . Cela provoque la méthode Actualiser () à être appelée beaucoup moins. Mais je n'ai aucun contrôle sur ceci.

    d'une autre manière?

    Je me demandais s'il y avait une autre façon de le faire, cela utiliserait la JACA. classes simultanées. Mais je ne pouvais pas trier dans le cadre des exécutants ce qui serait celui que je devrais commencer.

    Est-ce que l'une d'entre vous a une expérience avec un cas d'utilisation similaire?

    merci


0 commentaires

5 Réponses :


1
votes

Je fournirais un nouveau degré de déconnexion entre l'interface graphique et les commandes en utilisant une file d'attente.

Si vous utilisez un blockingQueue entre les deux processus. Chaque fois que les commandes changent, vous pouvez publier les nouveaux paramètres sur la file d'attente.

Votre composant graphique peut lire la file d'attente chaque fois qu'il aime et agir sur les événements arrivants ou les jeter si nécessaire.


0 commentaires

1
votes

Je regarderais dans SwingWorker.Publish () ( http://docs.oracle.com/javase/6/docs/api/javax/swing/swingworker.html )

Publier permet au fil d'antécédents d'un objet SwingWorker de causer des appels à la méthode de processus (), mais tous les appels de publication () n'appellent pas dans un processus de processus (). Si plusieurs appels de processus sont effectués avant le processus de processus () et peut être appelé à nouveau, SwingWorker concaténe les paramètres utilisés pour plusieurs appels publiés dans un appel à traiter.

J'ai eu une boîte de dialogue de progrès qui affichée des fichiers traités; Les fichiers ont été traités plus rapidement que l'interface utilisateur pourraient les suivre et je ne voulais pas que le traitement ralentit pour afficher les noms de fichiers; J'ai utilisé cela et j'ai eu le processus affichant uniquement le nom de fichier final envoyé au processus (); Tout ce que je voulais dans ce cas, c'était d'indiquer à l'utilisateur où le traitement actuel était, ils n'allaient pas lire tous les noms de fichiers de toute façon. Mon UI a travaillé très bien avec cela.


5 commentaires

Merci! Serait-il applicable pour autre chose qu'une interface graphique?


Je ne pense pas que processus et publie est ce que l'OP recherche. Vous ne pouvez pas mettre à jour un swingworker avec de nouveaux événements. Dans ce cas, le problème est différent: le traitement est plus lent que les actions déclenchées par l'interface utilisateur.


@Andrewmao Bon point - Publier / processus met à jour une interface utilisateur dû au traitement, et ce problème inclut la mise à jour du traitement de l'interface utilisateur. Les événements générés par l'interface utilisateur sont déjà mis en file d'attente, bien sûr; Peut-être que le programme pourrait avoir reçu des événements de curseur jusqu'à ce qu'il n'y ait plus d'événements dans la file d'attente, puis commencez le traitement de la valeur la plus récente, car ceux-ci ne sont plus pertinents. Je pense donc que mon idée est pertinente pour mettre à jour l'interface utilisateur, mais vous avez raison de prendre en compte la question de l'interface utilisateur de l'interface utilisateur.


@ Jean-Yves Je ne suis pas familier avec SwingWorker pour autre chose qu'une interface utilisateur, bien que je ne sache pas que cela ne peut pas être fait. J'ai pensé à une autre façon de faire des choses: laissez le curseur lancer une minuterie; Si la minuterie est déjà en cours d'exécution, mettez à jour son délai d'attente. Lorsque la minuterie expire, commencez le traitement. De cette façon, seul le dernier dans une série d'événements de curseur en file d'attente est traité.


@ Jean-Yves regarde aussi docs.oracle.com/javase/tatuly /uiswing/components/slider.ht ml Pour plus d'informations sur l'événement de changement que vous pouvez écouter du curseur; Il présente un exemple de celui qui n'est intéressé que dans le "dernier" changement fait, c'est-à-dire non intéressé par tous les événements qui se produisent tout en faisant glisser vers une valeur finale, mais intéressé par cette valeur lorsque le glissement s'arrête.



1
votes

Vérifiez la mise en œuvre de javax.swing.swingworker (code source dans le JAVA JDK), En mettant l'accent sur la prise de main entre deux méthodes: publier et processus .

Ceux-ci ne seront pas directement applicables, selon votre problème, mais ils démontrent comment vous pouvez faire la queue (publication) mises à jour sur un thread de travailleur, puis les servir dans votre thread de votre travailleur (processus).

Comme vous n'avez besoin que de la dernière demande de travail, vous n'avez même pas besoin d'une file d'attente pour votre situation: Ne gardez que la dernière demande de travail. Échantillon que "Dernière demande" sur une petite période (1 seconde), pour éviter d'arrêter / redémarrer plusieurs fois toutes les 1 seconde, et s'il est changé, arrêtez le travail et redémarrez.


La raison pour laquelle vous ne voulez pas utiliser Publier / le processus est que processus fonctionne toujours sur le thread d'expédition Swing Event Dispatch - Pas du tout adapté aux calculs de longue course.


1 commentaires

Cela dépend de la façon dont il l'utilise - si tout le processus fait la mise à jour de l'interface utilisateur, c'est exactement ce qu'il veut. Il n'a pas à (et ne devrait pas) faire son long calcul là-bas, je suis d'accord. Mais c'est parfaitement juste pour une partie de son problème.



4
votes

Si vous utilisez Swing , le swingworker fournit des capacités pour cela et que vous n'avez pas à gérer la piscine de fil vous-même.

feu un swingworker pour chaque demande. Si une nouvelle demande est entrée et que le travailleur n'est pas terminé, vous pouvez Annuler () IT, et simplement démarrer un nouveau swingworker . Concernant ce que l'autre affiche a dit, je ne pense pas publier () et processus () sont ce que vous recherchez (bien qu'ils soient aussi très utiles), puisqu'ils sont destinés à un cas où le travailleur pourrait déclencher des événements plus rapidement que l'interface graphique ne peut le traiter. xxx

à la fois l'action et le effectué () La méthode est exécutée sur le même thread, elles peuvent donc vérifier efficacement la référence pour savoir s'il existe un travailleur existant.

Notez que cela fait efficacement la même chose qui permet à une interface graphique d'annuler une opération existante, sauf L'annulation est effectuée automatiquement lorsqu'une nouvelle demande est tirée.


4 commentaires

bonne solution. Mais il n'est pas clair pour moi s'il y a une petite chance qu'après travailleur1.cancel () , travailleur1.done () est toujours exécuté, et il peut être exécuté après travailleur2.done () , c'est-à-dire qu'un seul résultat écrase un résultat plus récent. besoin d'un peu plus d'investigation ici.


Cela ne résout pas le problème de la manière de lotter les demandes, d'éviter la situation où vous annulez et redémarrez le fil de plusieurs fois sur une courte période.


Je ne pense pas que vous puissiez jamais éviter d'annuler les demandes, car vous ne savez jamais si une demande est la dernière. Bien sûr, vous pouvez attendre un peu avant de tirer des travailleurs, pour voir si plus de demandes arrivent, mais cela rendrait la mise à jour plus lente.


@ zhong.j.yu Si même possible, semble très improbable que travailleur2.doinbackground () prendra beaucoup plus de temps que travailleur1.cancel () . Aussi depuis le Annuler () , exécuter () et effectué () sont tous dans le thread d'expédition d'événement, il semble que annule () devrait empêcher effectué () à partir d'être appelé si Swing se synchronise correctement. Il n'y a pas d'état de course dans l'annulation ici.



0
votes

La clé ici est que vous souhaitez pouvoir annuler un calcul en cours. Le calcul doit fréquemment vérifier une condition pour voir si elle doit interrompre.

public void run()
    while(true)
        Param param = take();
        Result result = compute(param);
        if(result!=null)
            paint result in event thread


2 commentaires

Je ne sais pas «vérifier fréquemment une condition», aka scrutin; C'est ce que font les interruptions.


Comment interrompez-vous une boucle occupée :?)