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 :

questions sur les mutex, les singletons, boost etc.


Sujet :

C++

  1. #1
    Expert éminent Avatar de kain_tn
    Homme Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 564
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 564
    Points : 7 288
    Points
    7 288
    Par défaut questions sur les mutex, les singletons, boost etc.
    Bonjour.


    Je me retrouve face à un problème de conception...

    J'aimerais développer une classe servant à écrire des données dans un fichier (RAII)
    Seulement cette classe peut être accédée par plusieurs threads et je voudrais éviter que plus d'un thread à la fois modifie le fichier.

    L'idée que j'avais était de créer une fifo, et de faire en sorte que les threads poussent des données dans la fifo, tandis qu'un autre thread la dépilerait toutes les T périodes et écrirait les données dans le fichier.

    L'idée vous paraît-elle viable? Que feriez vous sinon?

    Pour ce qui est d'accéder à la fifo, il paraît évident qu'il ne faut pas que tous les threads y accèdent en même temps. Je pensais donc avoir recours à des mutex. J'ai commencé à regarder ceux de boost, mais j'avoue être un peu perdu dans la documentation (en gros, peu de cas pratiques, mais beaucoup de théorie...). Encore une fois, est-ce une bonne idée?

    A priori, ma fifo doit être statique pour être accédée de n'importe où non? Est-il préférable de la gérer dans une classe (singleton) ? Est-ce compatible (thread safe) avec ce que je veux faire plus haut?

    Bref, je ne sais pas si je suis très clair dans mes explications mais n'hésitez pas à me poser des questions!

    Merci d'avance!
    Copier c'est copier; voler c'est vendre un CD une vingtaine d'euros!


    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    int main(int argc, char **argv) {
     
        printf("So long, and thanks for the fish, Dennis...\n");
        return 0;
    }

  2. #2
    Expert éminent Avatar de kain_tn
    Homme Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 564
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 564
    Points : 7 288
    Points
    7 288
    Par défaut
    Bon eh bien en fait j'ai résolu mon problème

    Je ne suis pas passé par les mutex de boost mais j'ai créé ma propre classe de mutex et de lock:

    En gros, je crée ma classe Writer qui est un singleton:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class Writer: public Singleton<Writer>
    Ensuite je crée une classe Mutex, qui contient un booléen mLocked et trois méthodes isLock(), lock() et unlock():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     class Mutex{
     
    	private:
    		bool mLocked;
     
    	public:
    		Mutex()
    		:mLocked(false){
    		}
     
    		bool lock();
    		bool unlock();
    		bool isLocked()const;
     };
    Je crée également une classe Lock, qui prend une référence sur un object Mutex dans son constructeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
     class Lock{
     
     	private:
     		Mutex& mMutex;
     
     		void lock(Mutex& mutex){
     			mutex.lock();
     		}
     
     		void unlock(Mutex& mutex){
     			mutex.unlock();
     		}
     
     	public:
     		Lock(Mutex& mutex)
     		:mMutex(mutex){
     			lock(mutex);
     		}
     
     		~Lock(){
     			unlock(mMutex);
     		}
     
     };
    1. Le constructeur invoque la méthode lock() sur l'objet Mutex
    2. Le destructeur invoque la méthode unlock() sur l'objet Mutex


    Ainsi, je m'assure que le Mutex est bien déverrouillé à la mort de l'objet Lock.

    Je crée également une classe File, qui fonctionne sur le même principe; à savoir fopen() dans le constructeur, et fclose() dans le destructeur.

    Enfin, ma classe Writer contient une fifo (std::queue) ainsi qu'un objet Mutex. Elle contient également les méthodes push() et write().
    Ces deux méthodes ne se lancent que si Mutex.isLocked() vaut false.
    Elle instancient ensuite un objet Lock Cet objet va donc verrouiller le mutex et le déverrouiller dès que la méthode en cours sera terminée.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    bool push(const std::string& data){
     
    			if( !mMutex.isLocked() ){
     
    				Lock l(mMutex); // Lock the mutex
                                    if( !mMutex.isLocked() )
    					return false;
     
    				mData.push( data );
    				return true;
    			}
     
    			return false;
    		}
     
    ...
    Copier c'est copier; voler c'est vendre un CD une vingtaine d'euros!


    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    int main(int argc, char **argv) {
     
        printf("So long, and thanks for the fish, Dennis...\n");
        return 0;
    }

  3. #3
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Tu as l'air de t'en être sorti tout seul, félicitations .

    Toutefois, j'attire ton attention sur plusieurs choses :
    - tu as un peu réinventé la roue, il y a déjà tout ça dans boost.
    - comment fais-tu effectivement l'écriture dans le fichier ? Il y a un thread en attente active, ou quelque chose du genre ?
    - l'appelant doit à chaque fois vérifier que push a réussi, gérer des retry lui-même.

    Le premier point n'est pas très important.

    Pour le deuxième, il faudrait que tu passes par une condition variable, avec un réveil du thread chargé d'écrire dans le fichier, uniquement lorsque des données ont été rajoutées dans la queue (logiquement, dans la méthode push).

    Pour le troisième, j'ai un peu de mal à suivre l'idée. Tes threads font des choses critiques, et ne peuvent pas être bloqués plus d'un certain temps ? Si ce n'est pas le cas, autant mettre une attente infinie sur le mutex. Vu qu'il ne protège pas une ressource externe, mais seulement la queue, tu es tranquille, sauf erreur de programmation, il sera libéré à un moment ou un autre. Push réussira toujours, et tu n'auras pas à te préoccuper de gérer des réessais dans l'appelant à chaque push.

  4. #4
    Expert éminent Avatar de kain_tn
    Homme Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 564
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 564
    Points : 7 288
    Points
    7 288
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Toutefois, j'attire ton attention sur plusieurs choses :
    - tu as un peu réinventé la roue, il y a déjà tout ça dans boost.
    Oui je sais, mais le faire moi même m'a aidé à comprendre comment fonctionnent ces composants ainsi que la bibliothèque
    De plus, même si les options sont limitées comparées à boost, je ne dépends pas d'une bibliothèque externe.
    Citation Envoyé par white_tentacle Voir le message
    - comment fais-tu effectivement l'écriture dans le fichier ? Il y a un thread en attente active, ou quelque chose du genre ?
    Oui. Il y a un thread dédié à l'écriture. Lorsqu'il se réveille, il dépile la fifo.
    Citation Envoyé par white_tentacle Voir le message
    - l'appelant doit à chaque fois vérifier que push a réussi, gérer des retry lui-même.
    Oui. C'est un des points que je veux améliorer mais chaque chose en son temps (par contre, si tu as une idée... )

    Citation Envoyé par white_tentacle Voir le message
    Pour le deuxième, il faudrait que tu passes par une condition variable, avec un réveil du thread chargé d'écrire dans le fichier, uniquement lorsque des données ont été rajoutées dans la queue (logiquement, dans la méthode push).
    Pour la condition, je ne pense pas que ce soit un problème: en fait j'ai un système de "listener" sur mes "loggers", et je peux donc être prévenu si nécessaire qu'il y a eu un appel à push().

    Citation Envoyé par white_tentacle Voir le message
    Pour le troisième, j'ai un peu de mal à suivre l'idée. Tes threads font des choses critiques, et ne peuvent pas être bloqués plus d'un certain temps ? Si ce n'est pas le cas, autant mettre une attente infinie sur le mutex. Vu qu'il ne protège pas une ressource externe, mais seulement la queue, tu es tranquille, sauf erreur de programmation, il sera libéré à un moment ou un autre. Push réussira toujours, et tu n'auras pas à te préoccuper de gérer des réessais dans l'appelant à chaque push.
    OK, je vois un peu l'idée, mais tu ferais ça comment? Avec un while par exemple? Est-ce que ça ne risque pas de bouffer toutes les ressources du processeur?
    Tu verrais un truc comme ça?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    bool push(const std::string& data){
     
    	while( true ){
     
    		if( !mMutex.isLocked() ){
     
    			Lock l(mMutex); // Lock the mutex
    			if( !mMutex.isLocked() )
    				return false;
     
    			mData.push(data);
    			return true;
    		}else
    			sleep(2);
    	}
    }

    Merci d'avoir pris le temps de me lire en tous cas!
    Copier c'est copier; voler c'est vendre un CD une vingtaine d'euros!


    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    int main(int argc, char **argv) {
     
        printf("So long, and thanks for the fish, Dennis...\n");
        return 0;
    }

  5. #5
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Tu n'a pas besoin de while, ni d'attente active. Quand tu demandes à bloquer un mutex, si celui-ci est déjà bloqué, le thread est suspendu, il ne fait rien d'autre qu'attendre que le mutex se libère (et ce, sans consommer de processeur).

    Bien sûr, cela n'est vrai que si tu n'as pas mis de timeout à ton lock. Mais le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Lock l(mMutex); // Lock the mutex
    mData.push(data);
    return true;
    devrait suffire (à condition, bien sûr, que tu n'aies pas précisé de timeout sur ton lock, auquel cas tu n'es pas garanti que ton mutex est locké après le lock !)

  6. #6
    Expert éminent Avatar de kain_tn
    Homme Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 564
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 564
    Points : 7 288
    Points
    7 288
    Par défaut
    Je n'ai pas mis de timeout à mon lock, non
    Merci pour tes réponses, ça m'a beaucoup aidé!
    Copier c'est copier; voler c'est vendre un CD une vingtaine d'euros!


    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    int main(int argc, char **argv) {
     
        printf("So long, and thanks for the fish, Dennis...\n");
        return 0;
    }

  7. #7
    Membre habitué Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Points : 179
    Points
    179
    Par défaut
    juste pour rajouter un exemple de MUTEX (sous Windows) mais le concept est le même un peu partout avec d'autres fonctionnalités +/- nécessaires/utiles
    ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    Using Mutex Objects
    You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource. For example, if several threads share access to a database, the threads can use a mutex object to permit only one thread at a time to write to the database.
     
    The following example uses the CreateMutex function to create a mutex object and the CreateThread function to create worker threads.
     
    When a thread of this process writes to the database, it first requests ownership of the mutex using the WaitForSingleObject function. If the thread obtains ownership of the mutex, it writes to the database and then releases its ownership of the mutex using the ReleaseMutex function.
     
    This example uses structured exception handling to ensure that the thread properly releases the mutex object. The __finally block of code is executed no matter how the __try block terminates (unless the __try block includes a call to the TerminateThread function). This prevents the mutex object from being abandoned inadvertently.
     
     
    #include <windows.h>
    #include <stdio.h>
     
    #define THREADCOUNT 2
     
    HANDLE ghMutex; 
     
    DWORD WINAPI WriteToDatabase( LPVOID );
     
    void main()
    {
        HANDLE aThread[THREADCOUNT];
        DWORD ThreadID;
        int i;
     
        // Create a mutex with no initial owner
     
        ghMutex = CreateMutex( 
            NULL,              // default security attributes
            FALSE,             // initially not owned
            NULL);             // unnamed mutex
     
        if (ghMutex == NULL) 
        {
            printf("CreateMutex error: %d\n", GetLastError());
            return;
        }
     
        // Create worker threads
     
        for( i=0; i < THREADCOUNT; i++ )
        {
            aThread[i] = CreateThread( 
                         NULL,       // default security attributes
                         0,          // default stack size
                         (LPTHREAD_START_ROUTINE) WriteToDatabase, 
                         NULL,       // no thread function arguments
                         0,          // default creation flags
                         &ThreadID); // receive thread identifier
     
            if( aThread[i] == NULL )
            {
                printf("CreateThread error: %d\n", GetLastError());
                return;
            }
        }
     
        // Wait for all threads to terminate
     
        WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
     
        // Close thread and mutex handles
     
        for( i=0; i < THREADCOUNT; i++ )
            CloseHandle(aThread[i]);
     
        CloseHandle(ghMutex);
    }
     
    DWORD WINAPI WriteToDatabase( LPVOID lpParam )
    { 
        DWORD dwCount=0, dwWaitResult; 
     
        // Request ownership of mutex.
     
        while( dwCount < 20 )
        { 
            dwWaitResult = WaitForSingleObject( 
                ghMutex,    // handle to mutex
                INFINITE);  // no time-out interval
     
            switch (dwWaitResult) 
            {
                // The thread got ownership of the mutex
                case WAIT_OBJECT_0: 
                    __try { 
                        // TODO: Write to the database
                        printf("Thread %d writing to database...\n", 
                               GetCurrentThreadId());
                        dwCount++;
                    } 
     
                    __finally { 
                        // Release ownership of the mutex object
                        if (! ReleaseMutex(ghMutex)) 
                        { 
                            // Deal with error.
                        } 
                    } 
                    break; 
     
                // The thread got ownership of an abandoned mutex
                case WAIT_ABANDONED: 
                    return FALSE; 
            }
        }
        return TRUE; 
    }

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

Discussions similaires

  1. [XL-2010] Question sur Excel et les "Array-Entered Formulas" avec accolades
    Par balteo dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 06/08/2010, 19h33
  2. Question sur vista et les Exe
    Par restesouple dans le forum Windows Forms
    Réponses: 8
    Dernier message: 07/11/2007, 14h57
  3. [Java] Question sur Java et les états.
    Par Cassios dans le forum Autres outils décisionnels
    Réponses: 6
    Dernier message: 19/02/2007, 17h37
  4. [VBA-E]Encore une question sur csv et les dates
    Par vovor dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 14/10/2006, 10h43
  5. Question sur exports et les classes (pour une dll)
    Par DjPoke dans le forum Langage
    Réponses: 7
    Dernier message: 08/08/2005, 19h25

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