6
votes

En attente d'un avenir annulé pour finir réellement

J'ai un swingworker qui appelle du code qui ne vérifie pas l'interruption de thread. Après l'appel à travailleur.cancel (true) , le ouvrier.get.get () Method va lancer CANCellationException immédiatement (comme cela est censé) . Cependant, étant donné que le code de la tâche d'arrière-plan ne vérifie jamais son thread à interrompre, il continue joliment d'exécuter.

Y a-t-il un moyen standard d'attendre la tâche de fond en réalité finition? Je cherche à montrer un message "Annulation ..." ou quelque chose du genre et du bloc jusqu'à la résiliation de la tâche. (Je suis sûr que je pouvais toujours y accomplir avec un drapeau dans la classe ouvrière si nécessaire, à la recherche de toute autre solution.)


0 commentaires

3 Réponses :


1
votes

La chose la plus proche à une manière standard ou prêt à faire de cela est la propriété code> Progress ou la paire de méthodes de publication / processus fournie par swingworker code>. Vous pouvez définir cela sur une valeur "J'ai fini" à la fin de la méthode pour indiquer que les travaux d'arrière-plan sont effectués. Le fil d'attente sur le travailleur oscillant peut poser un message "Annulation ..." et vérifier périodiquement les progrès pour voir si elle a atteint l'achèvement. Si le fil d'attente est le swing Edt, vous devrez alors utiliser une minuterie pour vérifier périodiquement la propriété de progression et effacer le message d'annulation lorsque vous avez terminé.

Voici quelques exemples de code qui exécute un fil de fond obstiné, qui est annulé et puis attend jusqu'à ce que la progression atteigne 100. p>

@Test
public void testSwingWorker()
{
    SwingWorker worker = new SwingWorker() {

        @Override
        protected void process(List chunks)
        {
            for (Object chunk : chunks)
            {
                System.out.println("process: "+chunk.toString());
            }
        }

        @Override
        protected void done()
        {
            System.out.println("done");
        }

        @Override
        protected Object doInBackground() throws Exception
        {
            // simulate long running method
            for (int i=0; i<1000000000; i++)
            {
                double d = Math.sqrt(i);
            }
            System.err.println("finished");
            publish("finished");
            setProgress(100);
            return null;
        }
    };
    Thread t = new Thread(worker);
    t.start();

    try
    {
        worker.get(1, TimeUnit.SECONDS);
    }
    catch (InterruptedException e)        {
    }
    catch (ExecutionException e)        {
    }
    catch (TimeoutException e)        {
    }

    worker.cancel(true);

    // now wait for finish.
    int progress = 0;
    do
    {
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
        }
        progress = worker.getProgress();
        System.out.println(String.format("progress %d", progress));
    }
    while (progress<100);
}


5 commentaires

Ces deux idées sont des choses que je n'avais pas encore envisagées. Je n'aime pas utiliser les progrès pour la même raison que vous avez citées - cela nécessite un sondage. J'aime l'idée de publication / processus pour la plupart, mais il coule la logique que je veux faire lorsque la tâche est réellement fini sur la classe swingworker la classe elle-même. Pour cette même raison, j'utilise rarement la méthode terminée () et ajoutez généralement des auditeurs à l'objet travailleur.


Je suis d'accord avec vous, publie / processus est le meilleur des deux. En ce qui concerne le couplage, vous pouvez le découper en créant une sous-classe SwingWorker qui remplace la méthode de processus, écoute la valeur «J'ai terminé» et informez les auditeurs inscrits que la tâche de fond est réellement effectuée. Cela sépare la logique de l'ouvrier battant.


True, mais alors comment l'objet d'écoute serait-il entre-temps entre-temps jusqu'à ce qu'il reçoive la notification?


J'avais supposé que la logique serait exécutée sur l'EDT - c'est la force de l'utilisation de publications / processus - il saute les données publiées du fil de fond à l'EDT. Si vous avez besoin d'un thread séparé pour bloquer à la fin, enregistrez-vous un auditeur, qui contient le coundownlatch de votre propre réponse. Lorsqu'il est notifié, l'auditeur déclenche le loquet de compte à rebours, permettant ainsi au fil d'attente de continuer.


J'ai compris. Étant donné que le fil d'attente est l'EDT et qu'il bloque probablement en raison de la boîte de dialogue "Annulation ...", l'auditeur que vous parlez de ce qui fermerait la boîte de dialogue, ce qui empêche ainsi l'EDT. Cela dit, je ne suis toujours pas vendu à 100% sur cette option, au cas où le SwingWorker J'utilise est en réalité en utilisant la publication / processus à un autre but.



3
votes

J'ai joué un peu et voici ce que je suis venu. J'utilise un compte à rebours code> et expose essentiellement son attendre () code> méthode sous forme de méthode sur mon swingworker code> objet. Toujours à la recherche de meilleures solutions.

final class Worker extends SwingWorker<Void, Void> {

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1);

    @Override
    protected Void doInBackground() throws Exception {
        try {
            System.out.println("Long Task Started");

            /* Simulate long running method */
            for (int i = 0; i < 1000000000; i++) {
                double d = Math.sqrt(i);
            }

            return null;
        } finally {
            actuallyFinishedLatch.countDown();
        }
    }

    public void awaitActualCompletion() throws InterruptedException {
        actuallyFinishedLatch.await();
    }

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.execute();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {

        }

        System.out.println("Cancelling");
        worker.cancel(true);

        try {
            worker.get();
        } catch (CancellationException e) {
            System.out.println("CancellationException properly thrown");
        } catch (InterruptedException e) {

        } catch (ExecutionException e) {

        }

        System.out.println("Awaiting Actual Completion");
        try {
            worker.awaitActualCompletion();
            System.out.println("Done");
        } catch (InterruptedException e) {

        }
    }

}


0 commentaires

1
votes

Inspiré par la solution de Paul Béning, je l'ai un peu amélioré pour devenir une classe, vous pouvez sous-classe pour obtenir la funcitonalité souhaitée:

class AwaitingWorker<T,V> extends SwingWorker<T, V> {

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1);

    /**
     * Override this to do something useful
     */
    protected abstract void performOperation();

    @Override
    protected final T doInBackground() throws Exception {
        try {
            return performOperation();
        } finally {
            actuallyFinishedLatch.countDown();
        }
    }

    public void awaitActualCompletion() throws InterruptedException {
        actuallyFinishedLatch.await();
    }

}


2 commentaires

Je suppose que Cadré () appelle maintenant attendez-vousComplet () mais maintenant fait () peut être une tâche de course longue, car elle attend autour d'attendre que DoIndbackground () soit terminée après avoir été annulé avec Annuler (). Il va bloquer l'événementDispatThread


N'est-il pas possible d'annuler une tâche programmée pour l'exécution mais n'a pas été lancée? On dirait que l'attente d'exécution a besoin de vérifier en quelque sorte qu'une tâche n'a pas été annulée avant son départ avant d'appeler attendre.