2010-04-15 4 views
0

Je suis novice en Java et multithread. J'ai un problème suivant:Problèmes de programme récursifs/multithread

J'ai deux classes nommées Class A et Class B fonctionnant dans deux threads différents.

Class A a la méthode onNewEvent(). Une fois cette méthode appelée, elle demandera Class B de faire un peu de travail. Dès que la classe B termine le travail, il appelle la méthode onJobDone() définie également dans Class A.

Maintenant, voici le problème: ce que je veux est de créer un nouveau travail dans la méthode onJobDone() et de l'envoyer à Class B.

Voici ce que je fais (pseudocode) dans la séquence d'exécution

A.onNewEvent(){ 
    //create job 
    //ask B to do it 
    B.do() 
} 

B.do{ 
    // Do some stuff 
    A.jobDone() 
} 

A.onJobDOne(){ 
    B.do() //doItAgain 

    // print message "Thank you for doing it" 
} 

Le problème est que le message « Merci pour le faire » n'est jamais imprimé. En fait, lorsque la méthode onJobDone() est invoquée, elle appelle B.do() car B.do() est très rapide. Il appelle immédiatement onJobDone(), de sorte que le flux d'exécution ne parvient jamais à la partie PRINT MESSAGE du code.

Je suppose que c'est l'un des méchants problèmes multithreading.

Toute aide serait appréciée.

+9

Ce problème n'est pas lié au multithreading, vous venez de faire un appel récursif infini. – kahoon

+0

affiche le message avant le B.do() onJobDone() –

+1

B.do() ne se termine jamais, une fois implicitement toujours appelé B.Do(). Vous devriez vous retrouver avec une exception stackoverflow quelque temps –

Répondre

3

Ce n'est pas un problème mutli-thread, vous venez de créer une boucle infinie. B.do appelle A.onJobDone qui appelle B.do qui appelle A.onJobDone etc. Ainsi, l'exécution n'atteindra jamais la ligne de message d'impression. Vous avez besoin d'une condition de rupture de sorte que, dans onJobDone, vous puissiez décider si vous voulez 'doItAgain'. À un certain moment, vous déciderez de ne plus le faire et à ce moment votre code atteindra la ligne de message d'impression.

Cela peut être utile si vous décrivez ce que vous essayez d'obtenir et nous pourrions vous donner quelques indications sur la meilleure façon de l'implémenter. Je ne suis pas sûr si la façon dont vous essayez de résoudre votre problème est vraiment la meilleure.

+0

la raison pour laquelle je suis allé route multithread était d'assurer qu'une fois que j'envoie un travail à la classe B ... Je peux continuer avec mon flux d'exécution normal en classe A? Est-il possible de spécifier que onJobDone() n'est pas appelé par le thread B avant que la dernière instruction de la méthode ne soit exécutée? – Chronos

+0

Pouvez-vous poster un code réel, je serais intéressé de voir comment vous implémentez le multi-threadedness de ce que vous voulez dire par continuer avec le flux d'exécution normal dans la classe A (votre pseudo code ne montre aucune autre exécution en classe A autre qu'écouter des événements faits par le travail). – DaveJohnston

0

Voici une suggestion

A.onNewEvent(){ 
    //create job 
    //ask B to do it 
    B.do() 

} 

B.do{ 
    // Do some stuff 
    A.onJobDone() 
} 

A.onJobDone(){ 
    // print message "Thank you for doing it" (B.do() has just completed, so it 
    //will print for every time you call B.do() 
    if (shouldDoJobAgain()) //it should stop some time, right? 
     B.do() 

}

+0

la raison pour laquelle je suis allé route multithread était de faire en sorte qu'une fois que j'envoie un travail à la classe B ... je peux continuer avec mon flux d'exécution normale dans la classe A? Est-il possible de spécifier que onJobDone() n'est pas appelé par le thread B avant que la dernière instruction de la méthode ne soit exécutée? – Chronos

+0

La logique qui détermine le nombre de fois que je veux l'exécuter est définie dans onJobDone() ... pas dans onNewEvent() ... car parfois, le travail peut être envoyé pour exécution .. mais cela ne peut pas être fait. – Chronos

+0

@Chronos regarde mieux maintenant? –

0

Invoquer une méthode de classe n'est pas intrinsèquement liée à un fil. En d'autres termes, l'appel B.do() à A.onNewEvent() dans le thread 1 s'exécutera toujours dans le thread 1. En d'autres termes, les méthodes d'un objet peuvent être appelées à partir de n'importe quel thread.

Pour qu'une méthode s'exécute dans un thread donné, vous devez appeler cette méthode dans la méthode run du thread. Pour cela, vous devez définir: des méthodes sur B signalant l'arrivée d'un nouveau travail, un champ B pour contenir les informations sur le travail (en général, vous utiliseriez une file d'attente, mais un entier simple pourrait fonctionner ici) synchroniser correctement l'accès aux champs de B. Cela fait un petit peu depuis que je l'ai fait en Java (il peut y avoir une condition de course ou une impasse quelque part), mais je crois que quelque chose comme devrait fonctionner:.

class A { 
    ... 
    public void onEvent() { 
     b.addJob(); 
    } 
    // careful about making onJobDone synchronized 
    public void onJobDone() { 
     // do it again 
     b.addJob(); 
     ... 
    } 
} 

class B extends Thread { 
    A a; 
    int jobCount; 
    boolean work; 

    ... 
    public void run() { 
     shouldWork(true); 
     while (shouldWork()) { 
      while (haveJobs()) { 
       doingJob(); 
       ... 
       didJob(); 
      } 
      waitForWork(); 
     } 
    } 
    public synchronized void addJob() { 
     ++jobCount; 
     notify(); 
    } 
    protected synchronized boolean haveJobs() { 
     return jobCount > 0; 
    } 
    protected synchronized void doingJob() { 
     /* could also decrement 'jobCount' in didWork(), in which case 
      it will need to be made synchronized 
     */ 
     --jobCount; 
    } 
    protected void didJob() { 
     a.onJobDone(); 
    } 
    protected synchronized void waitForWork() { 
     while (! haveJobs()) { 
      try { 
       wait(); 
      } catch (Exception e) { 
     } 
    } 
    public synchronized void shouldWork(boolean w) { 
     work = w; 
    } 
    protected synchronized boolean shouldWork() { 
     return work; 
    } 
} 

Le problème principal à éviter est l'interblocage. La plupart des méthodes synchronisées ne provoqueront pas d'interblocage.waitForWork appelle wait, qui libère le moniteur afin que les autres threads puissent appeler avec succès addJob. Notez que ce qui précède fonctionnera toujours pour toujours, car la fin d'un travail entraîne la planification d'un nouveau travail. Les avantages par rapport à la façon dont vous l'avez codé est que A.onJobDone se terminera et vous n'obtiendrez pas de débordement de pile. Si vous voulez que chaque A.onEvent pour provoquer B à gérer n emplois, définir A comme:

class A { 
    int jobsPerEvent; 
    int remainingJobs; 
    ... 
    public void onEvent() { 
     synchronized (this) { 
      remainingJobs += jobsPerEvent; 
     } 
     b.addJob(); 
    } 
    public synchronized void jobsRemain() { 
     return remainingJobs > 0; 
    } 
    public void onJobDone() { 
     synchronized (this) { 
      --remainingJobs; 
     } 
     // do it again 
     if (jobsRemain()) { 
      b.addJob(); 
     } 
     ... 
    } 
} 
0

Une autre approche consiste à mettre en œuvre asynchronous method appels, refactoring la méthode asynchrone (B.do) dans une catégorie distincte.

class B { 
    A a; 
    Set jobs; 
    class BAlgo extends Thread { 
     public void run() { 
      // stuff originally in B.do 
      ... 
      done(); 
      jobs.remove(this); 
     } 
    } 

    public void do() { 
     BAlgo algo = this.new BAlgo(); 
     algo.start(); 
     jobs.add(algo); 
    } 
    protected void done() { 
     a.onJobDone(); 
    } 
    ... 
} 

Je suis sûr que refactoring la méthode do dans une classe qui implémente l'algorithme est un autre motif, mais je ne suis pas certain de son nom. Ce n'est pas tout à fait le bridge pattern, ni le template method pattern. Il suit presque le modèle proxy, mais pas tout à fait.