2010-09-01 2 views
3

Quelle est la meilleure approche pour atteindre la sécurité des threads pour des opérations plutôt simples?Devrais-je protéger les opérations sur les types primitifs avec des mutex pour être thread-safe en C++?

Tenir compte d'une paire de fonctions:

void setVal(int val) 
{ 
    this->_val = val; 
} 

int getVal() { 
    return this->_val; 
} 

Étant donné que même les affectations de types primitifs ne sont pas garantis atomiques, dois-je modifier tous les getter et setter dans le programme de la manière suivante pour être thread-safe ?

void setVal(int val) 
{ 
    this->_mutex.lock(); 
    this->_val = val; 
    this->_mutex.unlock(); 
} 

int getVal() { 
    this->_mutex.lock(); 
    int result = this->_val; 
    this->_mutex.unlock(); 
    return result; 
} 
+5

Notez qu'en général, pour la sécurité des exceptions et la clarté du code, il est préférable d'utiliser un 'scoped_lock' d'une sorte que de verrouiller et déverrouiller manuellement le mutex. –

+0

@James: Je suis assez novice avec des aspects pratiques ... est-il nécessaire de copier à un temporaire et de le renvoyer plutôt que de revenir directement ici? (ce qui nécessiterait un verrou de portée) Je suppose que la plupart des implémentations de compilateurs appliqueraient NRVO et donc cela n'aurait pas d'importance, mais je trouve que le 'résultat' est faux. –

+0

@Matthieu: Si vous utilisez un 'scoped_lock' vous pouvez simplement' return this -> _ val'. Si vous utilisez le verrouillage et le déverrouillage manuels, vous n'avez pas beaucoup de choix (sauf si vous aimez les blocages :-P). –

Répondre

6

Utilisez-vous _val dans plusieurs threads? Si non, alors non, vous n'avez pas besoin de synchroniser l'accès. S'il est utilisé à partir de plusieurs threads, alors oui, vous devez synchroniser l'accès, soit en utilisant un mutex, soit en utilisant un type atomique (comme std::atomic<T> en C++ 0x, bien que d'autres bibliothèques de threads aient aussi des types atomiques non standard).).

+0

Je me demande si c'est vraiment nécessaire si un autre thread n'a pas d'importance ce qui est retourné par 'getVal()'.Par exemple s'il imprime seulement le résultat à l'utilisateur en temps réel. – doc

+0

Je me pose la même question que doc. –

+0

@Piotr @doc: Dans ce cas, vous devez toujours synchroniser l'accès à l'objet afin d'éviter les lectures déchirées. –

1

Les mutex sont très coûteux, car ils peuvent être partagés entre les processus. Si l'état auquel vous limitez l'accès est seulement contraint aux threads dans votre processus actuel, optez pour quelque chose de moins lourd, comme une section critique ou un sémaphore.

-3
int getVal() { 
    this->_mutex.lock(); 
    int result = this->_val; 
    this->_mutex.unlock(); 
    return result; 
} 

Qu'espérez-vous accomplir avec cela? Bien sûr, vous avez arrêté this->_val de changer avant de sauvegarder dans result mais il peut encore changer avant que le résultat soit retourné, - ou entre le retour et l'assignation à ce que vous lui avez assigné - ou une microseconde plus tard. Peu importe ce que vous faites, vous allez juste obtenir un instantané d'une cible en mouvement. Faites avec.

void setVal(int val)   
{   
    this->_mutex.lock();   
    this->_val = val;   
    this->_mutex.unlock();   
} 

De même, qu'est-ce que vous achetez? Si vous appelez setVal(-5) et setVal(17) à partir de threads séparés en même temps, quelle devrait être la valeur après avoir terminé tous les deux? Vous avez fait des efforts pour vous assurer que le premier à commencer est aussi le premier à finir, mais comment est-ce que cela aide à obtenir la «bonne» valeur?

+3

Il ne s'agit pas de la valeur dans ce -> _ val. C'est pour protéger _val d'être rubish, car une assignation n'est pas atomique. J'ai utilisé temporaire en retour pour mettre le résultat sur la pile de sorte qu'un autre thread ne nuira pas à la valeur pendant le retour. Parce que je ne sais pas si la poussée du retour est atomique (?). Étant donné que l'affectation n'est pas atomique, je pourrais potentiellement obtenir un frottement en retour. Lorsque la variable 'result' est sur la pile, elle ne sera pas modifiée par un autre thread pendant le retour. – doc

+0

En d'autres termes, sans verrous, getVal() peut renvoyer les appels '-123232' entre les appels' setVal (-5) 'et' setVal (17) '. – doc

1

Sur les plates-formes x86 32 bits, les lectures et les écritures de valeurs 32 bits alignées sur une limite de 4 octets sont atomiques. Sur les plates-formes 64 bits, vous pouvez également compter sur des charges 64 bits et des magasins de valeurs alignées sur 8 octets. Les processeurs SPARC et POWER fonctionnent également comme ça. C++ ne fait aucune garantie de ce genre, mais en pratique aucun compilateur ne va se tromper, puisque chaque programme multi-thread non trivial repose sur ce comportement.

Questions connexes