2010-03-08 3 views
2

J'ai le code suivant qui réplique les événements manuels Windows et les réinitialisations automatiques.Question équivalente à l'événement pthread windows

class event 
{ 
public: 
    event(bool signalled = false, bool ar = true) : 
     _auto(ar), 
     _signalled(signalled) 
    { 
     pthread_mutex_init(&_mutex, NULL); 
     pthread_cond_init(&_cond, NULL); 
    } 

    ~event() 
    { 
     pthread_cond_destroy(&_cond); 
     pthread_mutex_destroy(&_mutex); 
    } 

    void set() 
    { 
     pthread_mutex_lock(&_mutex); 

     // only set and signal if we are unset 
     if (_signalled == false) 
     { 
      _signalled = true; 

      pthread_cond_signal(&_cond); 
     } 

     pthread_mutex_unlock(&_mutex); 
    } 

    void wait() 
    { 
     pthread_mutex_lock(&_mutex); 

     while (_signalled == false) 
     { 
      pthread_cond_wait(&_cond, &_mutex); 
     } 

     // if we're an autoreset event, auto reset 
     if (_auto) 
     { 
      _signalled = false; 
     } 

     pthread_mutex_unlock(&_mutex); 
    } 

    void reset() 
    { 
     pthread_mutex_lock(&_mutex); 

     _signalled = false; 

     pthread_mutex_unlock(&_mutex); 
    } 

private: 
    pthread_mutex_t _mutex; 
    pthread_cond_t _cond; 
    bool _signalled; 
    bool _auto; 
}; 

Ma question entoure la « optimisation » Je l'ai mis en place dans la méthode set() où je ne convoquent que pthread_cond_signal() si l'événement a été non signalés. Est-ce une optimisation valide ou ai-je introduit un défaut subtil en le faisant.

Répondre

1

Il y a certainement une différence de comportement due à cette "optimisation" si plusieurs threads attendent le même événement. Considérez cette séquence d'événements (mode de réarmement manuel):

thread 1 - wait 
thread 2 - wait 
thread 3 - wait 
thread 4 - set 
thread 4 - set 
thread 4 - set 
thread 4 - reset 

Avec votre code, pthread_cond_signal ne sera appelée une fois (un des fils débouchage 1-3); sans l'optimisation, il serait appelé 3 fois (débloquer tous les 3).

Je ne sais pas si c'est un "défaut" ou non, car je ne connais pas la sémantique précise de l'API Windows que vous émulez.

+0

C'est dans le cas des événements non-réinitialisation automatique, où vous voulez probablement seulement 1 action basée sur plusieurs ensembles avant la réinitialisation. donc l'ensemble() est en fait meilleur avec "l'optimisation". Généralement, les variables de condition doivent signaler un changement de condition. – stefaanv

+0

@David. Je suis d'accord avec votre évaluation et je vous remercie pour vos commentaires. La nature de réinitialisation automatique de l'événement est telle que l'appel défini sur un objet déjà signalé est un no-op. Donc je pense que le code que j'ai est correct, mais je suis ouvert aux arguments. – ScaryAardvark

+0

Si l'appel sur un événement de réinitialisation manuelle déjà défini est un noop, il ne s'agit pas d'une optimisation, mais d'un comportement correct. Dans le cas contraire, l'appel de l'ensemble avant la réinitialisation de l'événement débloquerait des threads supplémentaires. – nos

-1

Je qualifierais _signalled avec volatile, pour éviter toute astuces astucieuses de compilateur concernant cette variable.

+1

Je comprends volatil pour signifier que n'importe quelle variable marquée en tant que telle pourrait potentiellement être changée par le code non disponible au compilateur et en tant que tel le compilateur doit faire attention dans les optimisations. Cependant, tous les chemins de code sont disponibles pour le compilateur dans le code où _signalled est utilisé, donc j'aurais pensé que toutes les optimisations potentielles seraient valides. Je ne pourrais bien sûr pas avoir une compréhension ferme du concept volatile. – ScaryAardvark

+1

Pas nécessaire. Vos variables sont gardées par des verrous, les fonctions volatiles ne servent à rien ici, le compilateur n'est pas autorisé à mettre en cache la variable à travers les appels pthread_. – nos

+0

@ScaryAardvark: Si vous aviez raison, "volatile" ne suffirait pas. Par exemple, «volatile» ne garantit pas qu'un autre core * verra * le changement. Heureusement, vous êtes incorrect, et les autres mécanismes utilisés dans le code sont suffisants. Il n'y a pas de sémantique "volatile" qui soit suffisante pour résoudre les problèmes inter-threads, s'il y en avait. C'est pourquoi nous avons des mécanismes, tels que les mutex, qui sont suffisants. –

0

Pour les événements non réinitialisés automatiquement, vous ne réactivez qu'un seul thread sur un appel à set, mais vous arrêtez le blocage des threads supplémentaires. Cela ne me semble pas sain et crée une condition de race entre les serveurs et les wakers.