2010-10-04 4 views
11

Mon problème: J'ai un réseau sans fil 802.15.4 connecté à un port série (à l'aide d'un wrapper). Je peux envoyer des paquets dans le réseau et écouter les paquets entrants. Comme vous pouvez l'imaginer, c'est très asynchrone.Meilleure façon de synchroniser une tâche asynchrone

Voici la tâche: Je veux envoyer des commandes au réseau et attendre la réponse dans un appel de fonction. Par exemple: Je veux obtenir la température du nœud avec le réseau ID 1338.

double getTemperature(int id) throws Exception { .... } 

Y at-il une meilleure façon d'attendre un message de réponse autre que de faire tout cela « synchonized (objet) attendre (..) notifier (..) "des trucs?

Meilleures salutations, bigbohne

Peut-être ajouter un peu d'épices:

Tout cela devrait fin dans une interface web où l'utilisateur peut demander ces commandes (soit par ajax ou directement). J'ai également pensé à mettre en cache les valeurs de réponse dans une base de données. Mais pour certaines commandes vous MUSS avoir une réponse directe de succès/échec

+0

OK. Cette question pointe plus dans la direction de "Y at-il une astuce java cool résoudre ce problème?" – Bigbohne

+0

Celui-ci est aussi cool –

Répondre

7

Vous pouvez le faire avec un BlockingQueue pour ne pas avoir à gérer manuellement la synchronisation. La requête peut envoyer le message, puis appeler take() sur un BlockingQueue qui attendra qu'un élément soit présent. L'élément est la réponse qui est insérée dans la file d'attente par le programme d'écoute que vous avez sur le port lorsque la réponse est renvoyée.

Vous avez juste à coordonner afin que l'écouteur et le demandeur partagent la même file d'attente. Vous n'avez besoin que d'une file d'attente de taille 1 pour chaque requête afin de prendre en charge ce scénario.

// Send method 
BlockingQueue<Reply> q = new ArrayBlockingQueue<Reply>(1); 
serialCom.registerQueue(myRequest.getId()); 
serialCom.sendRequest(myRequest); 
return q.take(); 

//Listener 
BlockingQueue<Reply> q = queueMap.get(incomingMessage.getId()); 
q.add(incomingMessage.getReply()); 
+0

C'est exactement le "Special Java Thingy" que je cherchais! – Bigbohne

1

Le meilleur moyen est de conclure dans une classe de demande/réponse réutilisable. De cette façon, vous pouvez avoir un moyen standard d'interroger le port série et attendre une réponse. Cette classe, cependant, devra sans aucun doute utiliser le mot-clé synchronized et attendre et notifier les commandes quelque part dans son implémentation. C'est un élément clé de l'écriture Java et il est important de comprendre comment ils fonctionnent.

Si vous deviez concevoir une fonction comme vous le dites, vous devez vous assurer que vous l'appelez depuis un thread qui peut bloquer l'attente du résultat. La fonction devra implémenter une sorte de boucle d'attente à l'intérieur de celle-ci en attendant la réponse du port série. Il est donc important que tout ce qui est en train de tourner puisse aussi attendre.

Ceci ajoute également un point supplémentaire. Si vous avez plusieurs threads, vous devrez avoir une classe qui gère la communication du port série qui peut gérer plusieurs requêtes provenant de plusieurs threads et les mettre en file d'attente si nécessaire pour gérer la nature asynchrone du port série et du périphérique spécifique auquel vous vous connectez. Par exemple, il peut ne pas être approprié d'envoyer une deuxième commande avant que la réponse de la première soit lue complètement.

Il y a plusieurs problèmes compliqués ici, et il est important d'avoir un bon design en place qui les aborde tous d'une manière raisonnable.

+0

Merci pour votre contribution. Cela me ramène au stylo et au papier :) – Bigbohne

2

Je ne suis pas sûr de bien comprendre la question, mais j'utilise habituellement un Future<T> pour ce genre de tâches.

En interne, l'implémentation Future<T> utilise wait et notify et tout cela, mais l'interface elle-même devient plutôt propre.

Future<Double> getTemperature(int id); 

Dans votre code de communication, vous pouvez ensuite mapper les messages entrants vers des contrats à terme non remplis. Si la commande est garanti que vous pourriez faire quelque chose comme

class Something { 
    Map<Integer, Queue<Object>> requests; 

    synchronized Future<?> request(int id, Object data) { 
     MyFutureImpl future = new MyFuture(); 
     requests.get(id).add(future); 
     serializeAndSend(id, data); 
     return future; 
    } 

    void serializeAndSend(id, data) {...} 

    synchronized void response(int id, Object data) { 
     MyFutureImpl future = requests.get(id).remove(); 
     future.setValue(data); // This would fulfill the future, and any 
           // threads waiting in a get() will get the 
           // value and continue. 
    } 
} 

Où MyFutureImpl est un futur très basique mise en œuvre. Je suppose qu'il y a un fil de communication qui appelle response() quand un paquet est reçu.Je suppose également que la fonction serializeAndSend() gère l'écriture sur le client ou les blocs jusqu'à ce que l'opération d'écriture puisse être effectuée ou transmise à un fil de communication.

L'utilisation de structures de mappes et de files d'attente compatibles avec la simultanéité peut rendre certains synchronization inutiles. S'il n'y a qu'un seul appel en attente par ID, la file d'attente devient inutile bien sûr.

+0

Merci pour votre idée. devra parcourir votre code – Bigbohne

Questions connexes