M4104C – Exercice1 / La piscine

Dans cet exercice nous allons vous demander de modéliser le fonctionnement d’une piscine. Les clients de la piscine sont des threads, nous vous demandons donc d’écrire le code de ces threads correspondant au comportement décrit dans la suite de l’énoncé.

Un baigneur doit :

  • Arriver à la piscine ;
  • Prendre un panier ;
  • Trouver cabine et entrer dedans afin de se déshabiller ;
  • Libérer la cabine ;
  • Aller nager
  • Prendre une cabine et s’habiller ;
  • Rendre son panier ;
  • Quitter la piscine ;

Ressources critiques : certaines phases de ces activités mettent en jeu des ressources critiques, pour lesquelles les threads rentrent en compétitions :

  • la piscine dispose d’un certain nombre NbPaniers de paniers dans lesquels les baigneurs laissent leurs habits pendant le temps de la baignade ;
  • chaque baigneur se déshabille et se rhabille dans une cabine individuelle. La piscine dispose d’un nombre NbCabines de cabines.

Les phases de déshabillage et de rhabillage ne peuvent commencer que si certaines conditions sont réalisées :

  • chaque baigneur doit disposer d’un panier et d’une cabine pour commencer à se déshabiller ;
  • chaque baigneur ne doit disposer que d’une cabine pour commencer à se rhabiller.

Ces deux conditions sont donc éventuellement bloquantes.

Les fins des phases de déshabillage et de rhabillage ne sont pas bloquantes mais correspondent à la libération de ressources critiques :

  • chaque baigneur rend sa cabine quand il a fini de se déshabiller ;
  • chaque baigneur rend sa cabine et son panier quand il a fini de se rhabiller.

Le code du baigneur peut être le suivant :

//arrivée
sleep (XXX) ;
// déshabillage
DebutDeshabiller();
sleep(XXX);
FinDeshabiller();
//baignade
sleep (XXX) ;
// rhabillage
DebutRhabiller() ;
sleep(XXX) ;
FinRhabiller() ;
//départ
sleep (XXX) ;

Ecrire le corps des fonctions : DebutDeshabiller (), FinDeshabiller (), DebutRhabiller () et FinRhabiller () selon les spécifications précédentes.

M4104C – EXERCICE2 / Lecteur / rédacteur

A. Utilisation d’un unique sémaphore en exclusion mutuelle (Mut)

Coté lecteur :

T Lire ()
    Mut.P();
        T info = RetirerInfo ();
    Mut.V ();
    return info;

Coté rédacteur :

void Ecrire (const T & info)
    Mut.P();
        PoserInfo (info);
    Mut.V();

Montrer que l’on peut obtenir un interblocage de processus.

B. Utilisation de plusieurs sémaphores

La solution ci-dessous est inspirée de celle vue en cours, malheureusement elle n’est pas fonctionnelle. Dites pourquoi et corrigez la.

Indication : on donnera toujours priorité aux lecteurs par rapport aux rédacteurs.

Init : MutRedPresent = 0 ;  MutRedEcr = 0 ; MutLect = 0; 

Coté lecteur :

MutRedPresent.P ();
MutLect.P();
    T info = RetirerInfo ();
MutLect.V ();
return info ;

Coté rédacteur :

MutRedEcr.P();
    PoserInfo (info) ;
MutRedEcr.V();
MutRedPresent.V ();

M4104C – EXERCICE3 / Le diner des philosophes

Problème :

La situation est la suivante :

  • cinq philosophes (initialement mais il peut y en avoir beaucoup plus) se trouvent autour d’une table ;
  • chacun des philosophes a devant lui un plat de spaghetti ;
  • à gauche de chaque plat de spaghetti se trouve une fourchette.

Un philosophe n’a que trois états possibles :

  • penser pendant un temps indéterminé ;
  • être affamé (pendant un temps déterminé et fini sinon il y a famine) ;
  • manger pendant un temps déterminé et fini.

Des contraintes extérieures s’imposent à cette situation :

  • quand un philosophe a faim, il va se mettre dans l’état « affamé » et attendre que les fourchettes soient libres ;
  • pour manger, un philosophe a besoin de deux fourchettes : celle qui se trouve à gauche de sa propre assiette, et celle qui se trouve à droite (c’est-à-dire les deux fourchettes qui entourent sa propre assiette) ;
  • si un philosophe n’arrive pas à s’emparer d’une fourchette, il reste affamé pendant un temps déterminé, en attendant de renouveler sa tentative.

Le problème consiste à trouver un ordonnancement des philosophes tel qu’ils puissent tous manger, chacun à leur tour. Cet ordre est imposé par la solution que l’on considère comme celle de Dijkstra avec sémaphores ou Courtois avec des compteurs.

Le corps du processus philosophe peut être le suivant :

Philo(i)
{
    while(true)
    {
        pense( );
        prendre_fourchette( i );
        manger ( );
        poser_fourchette( i );
    }
}

Conséquence, il faut écrire les fonctions prendre_fouchette () et poser_fourchette ().

A. Première solution

On prend un sémaphore par fourchette (un mutex fait aussi l’affaire), chaque sémaphore est initialisé à 1.

sem fourch[ N ] = {1,........1}
prendre_fourchette (int i)
{
    fourch[i].P() ;
    fourch[(i+1)% N].P() ;
}

poser_fourchette (int i)
{
    fourch[i].V() ;
    fourch[(i+1)% N].V() ;
}

Monter que cette solution peut provoquer un interblocage de processus.

B. Deuxième solution

On modifie la solution 1 de sorte que les philosophes ne puissent pas prendre tous en même temps la fourchette de droite.

On introduit pour cela un sémaphore dont le compteur est égal à N-1

Implémenter cette solution.

C. Troisième solution

C’est la même chose que la première solution sauf qu’un philosophe est gaucher et ceci afin d’éviter l’interblocage.

Implémenter cette solution.

D. Quatrième solution 

On se centre ici sur les philosophes. Un sémaphore est attribué à chaque philosophe.

Etat = { PENSE , MANGE , A_FAIM}

Un philosophe qui veut prendre les fourchettes (donc manger) déclare qu’il a faim. Si l’un de ses deux voisins est en train de manger, il ne peut donc pas prendre les deux fourchettes pour l’instant et donc se met en attente. Si les deux philosophes à coté ne mangent pas alors il peut prendre les deux fourchettes et déclarer qu’il mange. Quand le philosophe a fini de manger, il déclare donc qu’il pense et regarde si ses deux voisins qui forcément ne pouvaient pas manger, en ont dès lors l’opportunité. Si c’est le cas il les réveille.

Implémenter cette solution.

M4104C-TP-Boost-Exercice1

Groupe de threads et functor

Dans cet exercice, le vector de pointeurs de threads n’est nécessaire que pour permettre au main-thread d’attendre qu’ils soient tous terminés avant de passer à la suite.

Il devient complètement inutile dès lors que les threads créés appartiennent au même groupe. Le main-thread peut alors se contenter d’attendre la fin de tous les threads du groupe.

La plupart des constructeurs de threads ont un paramètre de type Callable, c’est-à-dire pouvant être appelé au moyen de l’opérateur-fonction (). On peut donc utiliser un functor comme paramètre.

Travail à effectuer

Créer le projet GrpThreadsEtFctor. Vous pouvez vous servir de QT comme d’un éditeur de texte intelligent et réaliser les compilations et éditions de liens dans un terminal gnome ou faire tout avec QT. Cependant sachez que QT traite déjà ses propres threads qui seront en concurrence avec les vôtres.
Sachez également que lorsque vous importerez, dans QT, des fichiers dont les noms contiennent des majuscules, QT transformera ces majuscules en minuscules.

Télécharger le fichier Concurrence.cpp qui est le corrigé de l’exercice précédent. Le renommer en GrpThreadsEtFctor.cpp.

Modifier les propriétés du projet (ajout de la ligne
LIBS = /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.62.0 /usr/lib/x86_64-linux-gnu/libboost_system.so.1.62.0
dans le fichier .pro du projet).

Remplacer les fonctions Incr() et Decr() par les functors correspondants.

Supprimer tout ce qui concerne la classe vector, créer un groupe de threads et y ajouter dynamiquement tous les threads.

Compiler et tester.

M4104C-boost-Exo1-Corrigé

/**
 * @file   Concurrence.cxx
 *
 * @author D. Mathieu
 *         M. Laporte
 *
 * @date   17/02/2010
 *
 * @brief  Concurrence
 *
 * Acces protege par une section critique implementee par un lock_guard
 *
 */
#include <iostream>
#include <sstream>
#include <vector>

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>

using namespace std;
using namespace boost;

enum { KErrArg  = 253 };    // Erreur d'arguments de main()

typedef unsigned long long ULL_t;
typedef          long long  LL_t;

namespace
{
    volatile LL_t  a (0);
    ULL_t NbBoucles;
    mutex Mtx;

    class Incr
    {
      public :
        void operator () (void)
        {
            for (ULL_t i = 0; i < NbBoucles; ++i)
            {
                lock_guard <mutex> Lock (Mtx);
                ++a;
            }
        } // operator()()

    }; // Incr

    class Decr
    {
      public :
        void operator () (void)
        {
            for (ULL_t i = 0; i < NbBoucles; ++i)
            {
                lock_guard <mutex> Lock (Mtx);
                --a;
            }
        } // operator()()

    }; // Decr

} // namespace anonyme

int main (int argc, char * argv [])
{
    if (argc != 3)
    {
         cerr << "Usage : Concurrence <NbThreads> <NbBoucles>\n";
         return KErrArg;
    }
    unsigned NbThreads;
    {
         istringstream is (argv [1]);
         is >> NbThreads;
    }
    {
         istringstream is (argv [2]);
         is >> NbBoucles;
    }
    cout <<   "Nbre de threads : " << NbThreads
         << "\nNbre de boucles : " << NbBoucles << '\n';

    // vector <thread *> VThreads;
    thread_group threadGroup;

    for (unsigned i = NbThreads; i--; )
    {
        // VThreads.push_back (new thread (Incr));
        threadGroup.add_thread (new thread (Incr ()));
        // VThreads.push_back (new thread (Decr));
        threadGroup.add_thread (new thread (Decr ()));

    }
    // for (unsigned i = NbThreads * 2; i--; ) VThreads[i]->join ();
    threadGroup.join_all ();

    cout << "a = " << a << '\n';

    return 0;

} // main()





M4104C-TP-Boost-Exercice2

Dans ce qui suit, il s’agit d’établir un point de synchronisation entre N threads, simulant des activités que nous appellerons des passants.

L’activité de chaque passant (thread) est composée de trois parties :

  1. le traitement T1() à exécuter avant d’atteindre le point de synchronisation,

  2. l’arrivée au point de synchronisation, et l’attente que les N thread aient atteint ce point,

  3. le traitement T2() à exécuter après avoir quitté le point de synchronisation.

Les traitements T1() et T2() seront simplement simulés par des attentes (appels de sleep()).

Travail à effectuer

Créer le projet RDV_NThreadsBoost.

Télécharger les fichiers SleepChronogram.h et SleepChronogram.cpp.

Version a

Dans l’espace de noms anonyme du fichier RDV_NThreadsBoost.cpp, écrire la fonction RendezVous(), de profil

void RendezVous ();

qui est appelée par chaque thread lorsqu’il arrive au point de rendez-vous.

Cette fonction doit utiliser :

  • un compteur de threads encore attendus (variable globale initialisée au nombre total de threads et décrémentée chaque fois qu’un thread arrive au RDV),

  • une variable-condition qui bloque les threads tant que le compteur n’est pas nul et qui libère ensuite tous les threads bloqués (broadcast).

Dans l’espace de noms anonyme du fichier RDV_NThreadsBoost.cpp, écrire la classe (ou la struct) CPassant, qui contient :

  • un identifiant (entier naturel) qui s’incrémente automatiquement à chaque création d’un CPassant (utiliser une variable statique),

  • les caractéristiques de fonctionnement d’un thread : le temps qu’il met à arriver au point de rendez-vous, et le temps qu’il met à se terminer après être reparti du point de rendez-vous.

  • l’operator() qui représente le comportement de chaque passant.

    Le corps de cette fonction est composé de trois parties :

    1. l’arrivée au point de rendez-vous (sleep()),

    2. le blocage au point de rendez-vous (RendezVous()),

    3. le départ du point de rendez-vous(sleep()).

Ajouter un vector de CPassants.

Dans la fonction main(), lire au clavier le nombre de threads à lancer puis, pour chacun, les délais d’arrivée et de départ (on entrera ces données dans un fichier texte qui sera redirigé sur l’entrée standard du programme).

Ces délais sont placés dans une instance de CPassant, qui est ensuite rangée dans le vector.

Affichage : afin de suivre le chronogramme du déroulement des opérations, nous vous proposons ici d’utiliser une visualisation plus agréable que de faire afficher des tops d’horloge : à chaque seconde, chaque thread affiche son état (une lettre par exemple) sur une ligne qui lui est propre : le thread 1 progresse sur la ligne 1, le thread 2 progresse sur la ligne 2, etc.

Il n’affiche rien lorsqu’il est bloqué (sur un sémaphore, dans une variable condition, etc.)

En utilisant le fichier de données
StdIn
suivant qui contient :

3
3 5
4 6
5 2

redirigé sur l’entrée standard de l’exécutable RDV_NThreadsBoost qui est mis à votre
disposition et que vous devez
télécharger
(mais cela ne fonctionne pas), vous obtiendrez les chronogrammes suivants (à deux instants différents) :

AAA
AAAA
AAAA

Trois passants (threads) ont été lancés en même temps.

Le symbole 'A' est affiché à chaque seconde pendant que le thread Arrive vers le point de rendez-vous.

On voit que le thread 1 est bloqué alors que les deux autres ont continué à progresser pendant 1 seconde.

AAADDDDD
AAAADDDDD
AAAAADDX

On voit sur ce chronogramme que les trois threads sont repartis lorsque le troisième est arrivé au rendez-vous (au bout de 5 secondes).

Chacun a alors repris son exécution normale (Départ).

Le troisième s’est terminé au bout de 2 secondes (X), les autres continuent depuis 4 secondes.

Pour obtenir ces affichages, vous devez utiliser la NTCTS clrscr, la classe gotoxy et la fonction SleepChronogram(), toutes trois décrites dans le fichier SleepChronogram.h.

Version b

Modifier la fonction RendezVous() pour effectuer un réveil en cascade.

Version c

La bibliothèque Boost offre la fonction barrier qui permet de réaliser très simplement un rendez-vous de N threads !

La mettre en oeuvre.

M4104C-boost-Exo2-Corrigé

/**
 *
 * @file    SleepChronogram.h
 *
 * @author  M. Laporte, D. Mathieu
 *
 * @date    05/01/2007
 *
 * @version 2.0
 *
 * @brief   SleepChronogr() affiche periodiquement un caractere
 **/
#if !defined __SLEEP_CHRONOGR_H__
#define      __SLEEP_CHRONOGR_H__

#include <iostream>             // ios_base::failure
#include <string>

#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>

namespace nsUtil
{
    // L'injection de la NTCTS
    //
    //=========================//
    //                         //
    extern std::string clrscr; //
    //                         //
    //=========================//
    //
    // vide l'ecran et positionne le curseur en haut a gauche

    // La
    //
    //============//
    //            //
    class gotoxy; //
    //            //
    //============//
    //
    // est un injecteur qui positionne le curseur a l'ecran
    // en position x et y :
    //
    // cout << gotoxy (10, 20) << ...

    class gotoxy
    {
      public :
        gotoxy (int x, int y);
        friend std::ostream & operator << (std::ostream & os,
                                           const gotoxy & m);
      private :
        int m_x, m_y;

    }; // gotoxy()

    std::ostream & operator << (std::ostream & os, const gotoxy & m);

    // La fonction
    //
    //===============================================//
    //                                               //
    void SleepChronogram (                           //
                          unsigned Num,              //
                          unsigned & Col,            //
                          char Status,               //
                          unsigned Duree,            //
                          boost::mutex & Mutex,      //
                          unsigned Decal   = 10u,    //
                          unsigned Periode = 1u      //
                         )                           //
        throw (std::ios_base::failure);              //
    //                                               //
    //===============================================//
    //
    // fait dormir le thread qui l'appelle pendant Duree secondes au
    // moyen de la fonction ::sleep().
    //
    // Elle est destinee a tracer des chronogrammes de threads pendant
    // leur periode de sommeil, chronogrammes qui apparaissent comme
    // dans l'exemple ci-dessous :
    //
    // ------------ Haut de l'ecran ----------------------
    //                                  ^
    //                                  |
    //                                  |
    //                                Decal
    //                                  |
    //                                  |
    //                                  v
    // AAADDDDD
    // AAAADDDDD
    // AAAAADDX
    //
    //------------- Bas de l'ecran -----------------------
    //
    // Status  : caractere affiche en exclusion mutuelle.
    //           Il est sense representer l'etat courant du thread
    // Mutex   : mutex POSIX utilise pour l'affichage
    // Periode : periodicite de l'affichage (en secondes)
    // Duree   : duree totale de l'affichage (= du sommeil du thread)
    // Decal   : nombre de lignes vierges en haut de l'ecran
    // Num     : numero de la ligne d'affichage sur laquelle est affiche
    //           le caractere Status.
    //           Le curseur apparait donc a la ligne (Decal + Num).
    //           Num est en general le numero du thread (e partir de 0).
    // Col     : en entree : colonne du premier affichage du caractere
    //           Status
    //         : en sortie : position du curseur en fin d'affichage
    //
    // La fonction Sleep () est susceptible de lever deux exceptions
    // - std::ios_base::failure lors de l'injection du caractere
    // - nsUtil::CExcFct lors du (de)-verrouillage du mutex
    //   (voir CException.h)

} // namespace nsUtil

#endif /* __SLEEP_CHRONOGR_H__ */
/**
 *
 * @file    SleepChronogram.cpp
 *
 * @author  M. Laporte, D. Mathieu
 *
 * @date    05/01/2007
 *
 * @version 2.0
 *
 * @brief   SleepChronogr() affiche periodiquement un caractere
 **/
#include <iostream>

#include <unistd.h>             // sleep()

//#include "SleepChronogram.h"

using namespace std;
using namespace boost;
using namespace boost::posix_time;

string nsUtil::clrscr ("\033[2J");    //    Clear screen

#define GOTOXY nsUtil::gotoxy

GOTOXY::gotoxy (int x, int y) : m_x (x), m_y (y) {}

ostream & nsUtil::operator << (ostream & os, const gotoxy & m)
{
    return os << "\033[" << m.m_y << ';' << m.m_x << 'H';
}

#undef GOTOXY

void nsUtil::SleepChronogram (unsigned Num,
                              unsigned & Col,
                              char Status,
                              unsigned Duree,
                              boost::mutex & ioMutex,
                              unsigned Decal   /* = 10u */,
                              unsigned Periode /* = 1u  */)
    throw (std::ios_base::failure)
{
    for (unsigned i = Duree; i--; )
    {
        {
            lock_guard <mutex> Lock (ioMutex);
            cout << gotoxy (Col++, Num + Decal) << Status << flush;
        }
        this_thread::sleep (seconds (Periode));
    }

} // SleepChronogr()

/**
 * @file   RDV_NThreadsBoost_c.cpp
 *
 * @author D. Mathieu
 *         M. Laporte
 *
 * @date   08/10/2008
 *
 * @brief  Rendez-vous de N threads par la fonction barrier()
 *
 * Rendez-vous de N threads par la fonction barrier()
 *
 */
#include <iostream>
#include <sstream>
#include <vector>

#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/barrier.hpp>

//#include "SleepChronogram.h"

using namespace std;
using namespace boost;
using namespace nsUtil;

namespace
{
    enum { KExcArg  = 253 };    // Erreur d'arguments de main()

    mutex io_Mtx;
    barrier * pBarriere;

    void RendezVous ()
    {
        pBarriere->wait();

    } // RendezVous()

    class CPassant
    {
        static unsigned s_ID;

        unsigned  m_ID;
        unsigned  m_DelaiAvant;
        unsigned  m_DelaiApres;

      public :
        CPassant (const CPassant & P)
        :   m_ID (P.m_ID),
            m_DelaiAvant (P.m_DelaiAvant),
            m_DelaiApres (P.m_DelaiApres) {}

        CPassant (unsigned DelaiAvant, unsigned DelaiApres)
            : m_ID (++s_ID),
              m_DelaiAvant (DelaiAvant), m_DelaiApres (DelaiApres) {}

        void operator () ()
        {
            unsigned Col (1);

            SleepChronogram (m_ID, Col, 'A', m_DelaiAvant, io_Mtx);

            RendezVous();

            SleepChronogram (m_ID, Col, 'D', m_DelaiApres, io_Mtx);
            SleepChronogram (m_ID, Col, 'X', 1,            io_Mtx);

        } // operator()

    }; // CPassant

} // namespace

unsigned CPassant::s_ID (0);

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
         cerr << "Usage : RDV_NThreadsBoost\n";
         return KExcArg;
    }
    unsigned NbThreads;
    cin >> NbThreads;
    pBarriere = new barrier (NbThreads);

    vector <CPassant> VPassants;
    VPassants.reserve (NbThreads);

    for (unsigned DelaiAvant, DelaiApres, i (0); i < NbThreads; ++i)
    {
        cin >> DelaiAvant >> DelaiApres;
        VPassants.push_back (CPassant (DelaiAvant, DelaiApres));
    }
    thread_group GrpThreads;
    cout << clrscr << flush;

    for (unsigned i = NbThreads; i--; )
        GrpThreads.create_thread (VPassants [i]);

    GrpThreads.join_all ();

    cout << gotoxy (0, NbThreads + 12);

    return 0;

} // main()

M4104C-TP-Boost-Exercice3

Il s’agit de simuler par des threads des activités parallèles, que nous appellerons des baigneurs.

L’activité de chaque baigneur (thread) est constituée de différentes phases :

  1. le traitement initial effectué avant l’arrivée à la piscine, qui sera symbolisé par la lettre A (Arriver),

  2. le déshabillage, qui sera symbolisé par la lettre D,

  3. la baignade, qui sera symbolisé par la lettre B,

  4. le rhabillage, qui sera symbolisé par la lettre R,

  5. le traitement final effectué après le départ de la piscine, qui sera symbolisé par la lettre Q, (Quitter).

Chacun de ces cinq traitements sera simulé par un appel à la fonction sleep(), d’une durée différente pour chaque baigneur.

Ressources critiques : certaines phases de ces activités mettent en jeu des ressources critiques, pour lesquelles les threads entrent en compétitions :

  • la piscine dispose d’un certain nombre NbPaniers de paniers dans lesquels les baigneurs laissent leurs habits pendant le temps de la baignade,

  • chaque baigneur se déshabille et se rhabille dans une cabine individuelle.

    La piscine dispose d’un nombre NbCabines de cabines.

Les phases de déshabillage et de rhabillage ne peuvent commencer que si certaines conditions sont réalisées :

  • chaque baigneur doit disposer d’un panier et d’une cabine pour commencer à se déshabiller,

  • chaque baigneur ne doit disposer que d’une cabine pour commencer à se rhabiller.

Ces deux conditions sont donc éventuellement bloquantes.

Les fins des phases de déshabillage et de rhabillage ne sont pas bloquantes mais correspondent à la libération de ressources critiques :

  • chaque baigneur rend sa cabine quand il a fini de se déshabiller,

  • chaque baigneur rend sa cabine et son panier quand il a fini de se rhabiller.

Ces ressources critiques seront gérées au moyen de variable(s)-condition.

Travail à effectuer

Créer le projet PiscineThreadsBoost.

Télécharger les fichiers /tpAA-COO/usingthreads/usingthreads/RDV_NThreadsBoost/SleepChronogram.cpp”>SleepChronogram.cpp.

Dans l’espace de noms anonyme du fichier PiscineThreads.cpp, écrire les quatre fonctions de synchronisation DebutDeshabiller(), FinDeshabiller(), DebutRhabiller(), FinRhabiller(), de profils :

void DebutDeshabiller (void);
void FinDeshabiller   (void);
void DebutRhabiller   (void);
void FinRhabiller     (void);

Ces fonctions doivent utiliser :

  • deux compteurs NbPaniers et NbCabines, variables globales initialisées en début de programme et indiquant à l’instant t le nombre de ressources correspondantes encore disponibles,

  • une variable-condition qui bloque le thread si la ou les ressources dont il a besoin ne sont pas disponibles, et qui réveille les threads bloqués s’il rend une ou plusieurs ressources.

Dans l’espace de noms anonyme, écrire la classe (ou la struct) CBaigneur qui contient :

  • un identifiant (entier naturel) qui s’incrémente automatiquement à chaque création d’un CBaigneur (utiliser une variable statique),

  • les caractéristiques de fonctionnement d’un thread : le temps qu’il met à arriver à la piscine, le temps du déshabillage, le temps de la baignade, le temps du rhabillage, le temps qu’il met à se terminer après être reparti de la piscine,

  • l’operator() qui représente le comportement de chaque baigneur.

    Le corps de cette fonction est composé de cinq parties :

    1. l’arrivée à la piscine (sleep()),

    2. le déshabillage

      DebutDeshabiller()

      sleep()

      FinDeshabiller()

    3. la baignade (sleep()),

    4. le rhabillage,

      DebutRhabiller()

      sleep()

      FinRhabiller()

    5. le départ de la piscine (sleep()),

Ajouter un vector de CBaigneurs.

Dans la fonction main(), lire au clavier :

  • le nombre NbPaniers de paniers disponibles,

  • le nombre NbCabines de cabines disponibles (NbCabines < NbPaniers),

  • le nombre de baigneurs (threads) à lancer,

  • pour chaque baigneur, les 5 délais de son activité (on entrera ces données dans un fichier texte qui sera redirigé sur l’entrée standard du programme).

    Ces délais sont placés dans une instance de CBaigneur, qui est ensuite rangée dans le vector.

Comme dans l’exercice “Rendez-vous entre N threads“, l’affichage pourra être réalisé de façon agréable au moyen des outils mis à votre disposition dans les fichiers SleepChronogram.h et SleepChronogram.cpp.

Vous pouvez de nouveau /tpAA-COO/usingthreads/usingthreads/PiscineThreadsBoost/PiscineThreadsBoost.a”>-->télécharger et tester l’exécutable PiscineThreadsBoost qui est mis à votre disposition (mais qui ne se télécharge pas.

M4104C-boost-Exo3-Corrigé

/**
 *
 * @file    sleepChronogram.h
 *
 * @author  M. Laporte, D. Mathieu
 *
 * @date    05/01/2007
 *
 * @version 2.0
 *
 * @brief   SleepChronogr() affiche periodiquement un caractere
 **/
#if !defined __SLEEP_CHRONOGR_H__
#define      __SLEEP_CHRONOGR_H__

#include <iostream>             // ios_base::failure
#include <string>

#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>

namespace nsUtil
{
    // L'injection de la NTCTS
    //
    //=========================//
    //                         //
    extern std::string clrscr; //
    //                         //
    //=========================//
    //
    // vide l'ecran et positionne le curseur en haut a gauche

    // La
    //
    //============//
    //            //
    class gotoxy; //
    //            //
    //============//
    //
    // est un injecteur qui positionne le curseur a l'ecran
    // en position x et y :
    //
    // cout << gotoxy (10, 20) << ...

    class gotoxy
    {
      public :
        gotoxy (int x, int y);
        friend std::ostream & operator << (std::ostream & os,
                                           const gotoxy & m);
      private :
        int myx, myy;

    }; // gotoxy()

    std::ostream & operator << (std::ostream & os, const gotoxy & m);

    // La fonction
    //
    //===============================================//
    //                                               //
    void sleepChronogram (                           //
                          unsigned num,              //
                          unsigned & column,            //
                          char status,               //
                          unsigned oneDuration,            //
                          boost::mutex & oneMutex,      //
                          unsigned decal   = 10u,    //
                          unsigned period = 1u      //
                         )                           //
        throw (std::ios_base::failure);              //
    //                                               //
    //===============================================//
    //
    // fait dormir le thread qui l'appelle pendant oneDuration secondes au
    // moyen de la fonction ::sleep().
    //
    // Elle est destinee a tracer des chronogrammes de threads pendant
    // leur periode de sommeil, chronogrammes qui apparaissent comme
    // dans l'exemple ci-dessous :
    //
    // ------------ Haut de l'ecran ----------------------
    //                                  ^
    //                                  |
    //                                  |
    //                                decal
    //                                  |
    //                                  |
    //                                  v
    // AAADDDDD
    // AAAADDDDD
    // AAAAADDX
    //
    //------------- Bas de l'ecran -----------------------
    //
    // status  : caractere affiche en exclusion mutuelle.
    //           Il est sense representer l'etat courant du thread
    // oneMutex   : mutex POSIX utilise pour l'affichage
    // period : periodicite de l'affichage (en secondes)
    // oneDuration   : duree totale de l'affichage (= du sommeil du thread)
    // decal   : nombre de lignes vierges en haut de l'ecran
    // num     : numero de la ligne d'affichage sur laquelle est affiche
    //           le caractere status.
    //           Le curseur apparait donc a la ligne (decal + num).
    //           num est en general le numero du thread (e partir de 0).
    // column     : en entree : colonne du premier affichage du caractere
    //           status
    //         : en sortie : position du curseur en fin d'affichage
    //
    // La fonction Sleep () est susceptible de lever deux exceptions
    // - std::ios_base::failure lors de l'injection du caractere
    // - nsUtil::CExcFct lors du (de)-verrouillage du mutex
    //   (voir CException.h)

} // namespace nsUtil

#endif /* __SLEEP_CHRONOGR_H__ */
/**
 *
 * @file    sleepChronogram.cpp
 *
 * @author  M. Laporte, D. Mathieu
 *
 * @date    05/01/2007
 *
 * @version 2.0
 *
 * @brief   SleepChronogr() affiche periodiquement un caractere
 **/
#include <iostream>

#include <unistd.h>             // sleep()

//#include "sleepChronogram.h"

using namespace std;
using namespace boost;
using namespace boost::posix_time;

string nsUtil::clrscr ("\033[2J");    //    Clear screen

#define GOTOXY nsUtil::gotoxy

GOTOXY::gotoxy (int x, int y) : myx (x), myy (y) {}

ostream & nsUtil::operator << (ostream & os, const gotoxy & m)
{
    return os << "\033[" << m.myy << ';' << m.myx << 'H';
}

#undef GOTOXY

void nsUtil::sleepChronogram (unsigned num,
                              unsigned & column,
                              char status,
                              unsigned oneDuration,
                              boost::mutex & ioMutex,
                              unsigned decal   /* = 10u */,
                              unsigned period /* = 1u  */)
    throw (std::ios_base::failure)
{
    for (unsigned i = oneDuration; i--; )
    {
        {
            lock_guard <mutex> oneLock (ioMutex);
            cout << gotoxy (column++, num + decal) << status << flush;
        }
        this_thread::sleep (seconds (period));
    }

} // SleepChronogram()

/**
 * @file   PoscineThreadsBoost.cpp
 *
 * @author D. Mathieu
 *         M. Laporte
 *
 * @date   23/09/2008
 *
 * @brief  Simulation du fonctionnement d'une piscine
 *
 *  Simulation du fonctionnement d'une piscine
 *  Gestion de plusieurs ressources de natures differentes
 */
#include <iostream>
#include <vector>

#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition.hpp>

//#include "CstCodErr.h"
//#include "SleepChronogram.h"

using namespace std;
using namespace boost;

using namespace nsUtil;             // KExcArg

namespace
{
    enum { KExcArg  = 253 };    // Erreur d'arguments de main()
    mutex io_Mtx;
    mutex mtxCond;
    condition_variable condition;

    unsigned nbBaskets;
    unsigned nbCubicles;

    void beginUndressing (void)
    {
        unique_lock <mutex> oneLock (mtxCond);
        while (nbCubicles == 0 || nbBaskets == 0) condition.wait (oneLock);

        --nbBaskets;
        --nbCubicles;

    } // beginUndressing()

    void endUndressing   (void)
    {
        unique_lock <mutex> oneLock (mtxCond);
        ++nbCubicles;

        condition.notify_all ();

    } // endUndressing()

    void beginDressingAgain   (void)
    {
        unique_lock <mutex> oneLock (mtxCond);
        while (nbCubicles == 0) condition.wait (oneLock);

        --nbCubicles;

    } // beginDressingAgain()

    void endDressingAgain     (void)
    {
        unique_lock <mutex> oneLock (mtxCond);
        ++nbCubicles;
        ++nbBaskets;

        condition.notify_all ();

    } // endDressingAgain()

    class Bather
    {
        static unsigned s_ID;

        unsigned myID;
        unsigned myArrivalDuration;
        unsigned myUndressingDuration;
        unsigned myBathDuration;
        unsigned myDressingAgainDuration;
        unsigned myEndingDuration;

      public :
        Bather  (unsigned arrivalDuration,
                 unsigned undressingDuration,
                 unsigned bathDuration,
                 unsigned dressingAgainDuration,
                 unsigned endingDuration)
            : myID (++s_ID),
              myArrivalDuration       (arrivalDuration),
              myUndressingDuration    (undressingDuration),
              myBathDuration          (bathDuration),
              myDressingAgainDuration (dressingAgainDuration),
              myEndingDuration        (endingDuration)          {}

        void operator ()()
        {
                unsigned Col (1);
            sleepChronogram (myID, Col, 'A', myArrivalDuration,
                         io_Mtx);

            beginUndressing();

            sleepChronogram (myID, Col, 'D', myDressingAgainDuration, io_Mtx);

            endUndressing ();

            sleepChronogram (myID, Col, 'B', myBathDuration,       io_Mtx);

            beginDressingAgain ();

            sleepChronogram (myID, Col, 'R', myDressingAgainDuration,   io_Mtx);

            endDressingAgain ();

            sleepChronogram (myID, Col, 'Q', myEndingDuration,          io_Mtx);

            sleepChronogram (myID, Col, 'X', 1,                   io_Mtx);

        } // operator()

   }; // Bather

} // namespace

unsigned Bather::s_ID (0);

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
         cerr << "Usage : PiscineThreadsBoost\n";
         return KExcArg;
    }
    unsigned nbThreads;

    cin >> nbBaskets >> nbCubicles >> nbThreads;

    cout << clrscr << flush;
    vector <Bather> vBathers;
    vBathers.reserve (nbThreads);
    for (unsigned arrivalDuration, undressingDuration, bathDuration,
                  dressingAgainDuration, endingDuration, i (0); i < nbThreads; ++i)
    {
        cin >> arrivalDuration    >> undressingDuration >> bathDuration
            >> dressingAgainDuration >>  endingDuration;

        vBathers.push_back (
              Bather (arrivalDuration, undressingDuration,
                         bathDuration,  dressingAgainDuration, endingDuration));
    }
    thread_group grpThreads;
        cout << clrscr << flush;

    for (unsigned i = nbThreads; i--; )
        grpThreads.create_thread (vBathers [i]);

    grpThreads.join_all ();

    cout << gotoxy (0, nbThreads + 12);

    return 0;

} // main()