2010-08-23 14 views
2

J'ai entendu dire que les mutex récursifs sont evil mais je ne peux pas penser à la meilleure façon de faire fonctionner cette classe sans un.Échange entre un mutex récursif V une classe plus complexe?

class Object { 
public: 
    bool check_stuff() { 
     lock l(m); 

     return /* some calculation */; 
    } 
    void do_something() { 
     lock l(m); 

     if (check_stuff()) { 
      /* ... */ 
     } 
     /* ... */ 
    } 
private: 
    mutex m; 
} 

La fonction do_something() va interblocage si m n'est pas récursive, mais quelle est l'alternative à moins d'avoir, disons, deux façons d'effectuer la vérification dont un ne se verrouille pas. Ce choix, comme le suggère le lien ci-dessus, rendra finalement la classe plus complexe, y a-t-il une solution plus simple qui ne nécessite pas de mutex récursif? Ou est-ce un mutex récursif parfois non que mal?

Répondre

4

Si vous avez besoin d'exposer check_stuff en public (ainsi que do_something qui appelle actuellement il), un simple refactoring vous voir à travers sans répéter les calculs:

class Object { 
public: 
    bool check_stuff() { 
     lock l(m); 

     return internal_check_stuff(l); 
    } 
    void do_something() { 
     lock l(m); 

     if (internal_check_stuff(l)) { 
      /* ... */ 
     } 
     /* ... */ 
    } 
private: 
    mutex m; 
    bool internal_check_stuff(lock&) { 
     return /* some calculation */; 
    } 
} 

(comme par @ réponse de Logan, en passant une référence de verrou à la fonction interne est juste une bonne idée pour éviter l'oubli des futurs mainteneurs ;-).

Les verrous récursifs ne sont nécessaires que si vous appelez une méthode qui verrouille une autre méthode (avec le même verrou), mais un peu de refactoring (déplacement de la fonctionnalité commune dans des méthodes privées qui ne verrouillent pas comme "rappels" ;-) supprime le besoin. Pas besoin de répéter une fonctionnalité - il suffit de rendre publiques des fonctions qui exposent exactement la même fonctionnalité (verrouillée) qui est également nécessaire (de manière non verrouillable car le verrou est déjà acquis) à partir d'autres fonctions, dans "semi-vide". coquilles "qui effectuent le verrouillage puis appeler ces fonctions privées, qui font tout le travail nécessaire. Les autres fonctions de la classe qui ont besoin d'une telle fonctionnalité mais qui veulent faire leur propre verrouillage peuvent simplement appeler directement les privées.

+0

Merci Alex, surtout pour une telle clarté. Je pense que c'est l'approche que je vais adopter. – Eoin

+0

@Eoin, vous êtes les bienvenus! –

0

Rendre check_stuff privé, et non verrouiller, et l'appeler simplement de do_something avec le mutex verrouillé. Un idiome commun à utiliser pour rendre ce plus sûr est de passer une référence à la serrure à check_stuff:

bool check_stuff(const lock&) 
{ 
    return /* some calculation */; 
} 

void do_something() 
{ 
    lock l(m); 
    if(check_stuff(l)) { // can't forget to lock, because you need to pass it in to call check_stuff 
     ... 
    } 
} 

check_stuff ne doit pas bloquer et renvoyer une bool de toute façon, toute valeur qu'elle retourne est obsolète au moment où vous regardez-la, ce qui la rend plutôt inutile pour les consommateurs sans accès à la serrure.

+0

Merci Logan. Dans mon cas particulier les appelants extérieurs de 'check_stuff' montraient simplement l'état sur une interface utilisateur donc sa validité n'était pas critique. Mais je comprends que votre commentaire soit tout à fait correct lorsque ce résultat serait fiable. – Eoin