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

C++ Discussion :

Asynchronisme avec boost::asio


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 65
    Par défaut Asynchronisme avec boost::asio
    Bonjour,

    J'essaye de regarder un peu ce qui existe au niveau d'implémentations asynchrones pour les communications socket, et il y a quelque chose que je ne comprends pas bien.

    Avant de me renseigner, j'ai commencé par développer mon propre système d'asynchronisme (côté client):
    - Une tâche qui fait évoluer une machine d'état et qui effectue des écritures sur socket.
    - Une tâche qui lit de façon bloquante les réponses.
    => Le principal problème est que la réception se fait dans un contexte différent.

    Maintenant, en me renseignant sur les méthodes proposées par boost::asio, je tombe sur ceci:
    Pour pallier à ce mode de fonctionnement (bloquant, synchrone), on pourrait lancer la réception de données dans un thread par exemple. On s'affranchirait ainsi du problème de la réception synchrone. Par contre, le développeur devra dans ce cas gérer les accès concurrents lui- même
    Cette citation confirme bien le problème de contexte constaté ci-dessus. Par contre, ce que je ne comprends pas, c'est que dessous, ils mettent par exemple l'API "async_read":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    void completion_handler(const boost::system::error_code& error)
    {
    	std::cout << "Terminé" << std::endl;
    }
    void Mafonction()
    {
    	//Lecture asynchrone
    	boost::asio::async_read(socket, boost::asio::buffer(msg), 
    		boost::bind(&completion_handler, boost::asio::placeholders::error) 
    		); 
    	std::cout << "Ca s'affiche ! " << std::endl;
    }
    Ici, une callback est passée en paramètre de la fonction async_read. Mais que fait réellement async_read ? J'ai tendance à penser qu'elle crée une tâche en sous-marin qui réceptionne ce qu'il y a dans la socket puis appelle la callback à son arrivée. Du coup, la callback serait appelée dans un contexte différent, et l'on peut imaginer des accès concurrent également entre la callback et la tâche qui a effectué l'émission. N'est-ce pas ?

    Julien.

  2. #2
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Salut,

    En fait Boost.Asio fait bien plus que ce que tu t'imagines en ce moment. Je vais tenter l'explication.

    Tout d'abord tu as l'objet io_service que tu as passé à ton socket (par exemple), cet objet est en réalité une file de tâche. En gros, à chaque fois que tu fais un appel asynchrone (async_read, async_write, ...), cette fonction met une tâche de lecture/écriture dans cette file. C'est entre autre pour ça que cette fonction retourne tout de suite vu qu'elle n'exécute pas la tâche. C'est également pour ça qu'il faut faire très attention à la durée de vie de tes objets, si tu passes un argument en ref à ton callback, il faut que celui-ci reste en vie jusqu'à ce que la tâche (de read ou write ou autre) soit réalisée. C'est notamment pour cette raison que beaucoup de choses sont allouées sur le tas lorsqu'on fait de la programmation asynchrone.

    Pour ce qui est du io_service, tu appelles la méthode run dessus, qui est bloquante et va exécuter toutes les tâches (et handler) jusqu'à ce qu'il n'y en ai plus dans la file. Et c'est seulement à ce moment là que toutes les tâches seront exécutées, pas avant.

    Si tu crées 4 threads qui appelle chacun la méthode run, alors chacun des threads iront se servir dans cette file, note que io_service est thread_safe (en tout cas la méthode run).

    En fait tout ceci fait partie du design pattern proactor que Boost.Asio implémente. Il faut un peu de temps pour comprendre toutes les subtilités mais c'est extrêmement puissant, si tu fais toute ton appli en asynchrone, tu peux alors faire le choix d'utiliser seulement un thread.

    Dernière chose, io_service attend que les lectures/écritures sur le socket, ou autre action bloquante, se terminent via un appel système équivalent à select.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 65
    Par défaut
    Merci pour la réponse.

    Donc, si je comprends bien, si une application fait appel à la fonction async_read par exemple, il doit faire appelle à la fonction run (qui est bloquante) derrière pour récupérer la réponse. Du coup, la réponse n'est pas récupérée dès qu'elle est arrivée mais dès que l'application fait appelle à run.

    J'essaye de comparer cette méthode avec celle mentionnée dans mon premier post, à savoir une tâche émettrice, et une tâche réceptrice qui fait appelle à une callback fournit lors de l'émission. Ici, la réponse est directement traitée dès son arrivée (dans un contexte différent par contre).

    Est-ce bien cela ?

    Julien.

  4. #4
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    À mon avis, tu n'as pas bien saisi le fonctionnement, mais ce n'est pas évident à comprendre. Dans une application typique avec Boost.Asio, le .run() ne devrait être appelé qu'à un seul endroit dans le code, la logique du code se développant au travers des appels asynchrones et des callbacks. En fait, généralement, un callback relance une autre opération asynchrone, ce qui fait que la file dans le io_service n'est jamais vide et le run() ne termine jamais.

    Ceci est vrai côté serveur où il y aura toujours au moins la requête asynchrone d'acceptation d'un nouveau client (et le callback associé) dans cette file.

    Côté client, le run se terminera quand tu auras terminé en totalité ce que tu dois faire au niveau du réseau. Donc je modifie légèrement mon assertion, le run(), lors d'une application client, peut être appelé a plusieurs endroit dans le code, après je te conseille de regrouper tout ça dans une même classe pour éviter la duplication de code.

    J'espère que c'est un peu plus clair. Note que l'asynchronisme n'implique pas forcément l'utilisation de plusieurs threads (mais tu le savais surement).

    Finalement, pour te corriger :

    Du coup, la réponse n'est pas récupérée dès qu'elle est arrivée mais dès que l'application fait appelle à run.
    C'est faux, généralement vu que tu fais un async_*, la fonction retourne tout de suite et ne place dans la file que le callback et les infos nécessaires pour effectuer le traitement asynchrone. On écoute rien du réseau. C'est seulement lorsque ton programme va lancer l'instruction run que celle-ci va écouter (via select ou équivalent) ce qui se passe sur le réseau et réellement appeler les callbacks,... Tu peux également en déduire qu'il n'y a aucun traitement asynchrone qui est réalisé en dehors des threads qui exécute la méthode run.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 65
    Par défaut
    Très bien, je te remercie pour toutes tes explications !

    A bientot,

    Julien.

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

Discussions similaires

  1. Echange d'objets avec Boost ASIO
    Par CocoLeNain dans le forum Boost
    Réponses: 2
    Dernier message: 08/02/2013, 09h51
  2. [BOOST.ASIO] Pb avec deadline_timer
    Par vdaanen dans le forum Boost
    Réponses: 1
    Dernier message: 31/08/2011, 12h22
  3. Impossible de compiler avec BOOST ASIO
    Par Sentenza31 dans le forum Débuter
    Réponses: 0
    Dernier message: 27/11/2010, 18h52
  4. Manipulation d'objets socket avec boost::asio
    Par K-you dans le forum Boost
    Réponses: 9
    Dernier message: 14/04/2010, 14h40
  5. Envoi de données entières avec boost::asio
    Par K-you dans le forum Boost
    Réponses: 5
    Dernier message: 28/03/2010, 09h07

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