2008-11-07 5 views
1

Cette question concerne l'utilisation des méthodes getter d'un objet singleton dans les threads de travail. Voici un code pseudo premier:Singleton getInstance dans les méthodes de travail de thread

// Singleton class which contains data 
class MyData 
{ 
    static MyData* sMyData ; 

    int mData1[1024]; 
    int mData2[1024]; 
    int mData3[1024]; 

    MyData* getInstance() 
    { 
     // sMyData is created in the very beginning. 
     return sMyData ; 
    } 

    void getValues(int idx, int& data1,int& data2,int& data3) 
    { 
     data1 = mData1[idx]; 
     data2 = mData2[idx]; 
     data3 = mData3[idx]; 
    } 

    int* getData1() 
    { 
     return &mData1[0]; 
    } 
} 

class MyThread 
{ 
    void workerMethod() 
    { 
     MyData* md = MyData::getInstance(); 

     int d1,d2,d3; 
     md->getValue(12, d1,d2,d3); 

     int* data1 = md->getData1(); 
     d1 = data1[34]; 
    } 
} 

Maintenant que vous voyez que j'ai des méthodes de lecture (tout en lecture seule), MyData :: getInstance(), MyData :: getValue() et MyData :: getData1() . La première question est de savoir comment ces méthodes sont thread-safe? Comme ils sont souvent appelés méthodes, la protection de ces méthodes avec mutex est quelque chose que j'essaie d'éviter.

La deuxième question est: quelle est la manière suggérée de lire des données à partir de sources centrales dans une application multi-thread, en particulier dans les méthodes de travail.

Merci!

Paul

Répondre

5

A condition qu'aucun autre thread va essayer d'écrire les données dans votre objet singleton, vous n'avez pas besoin de les protéger: par définition, plusieurs lecteurs en l'absence d'un écrivain est thread-safe . C'est un modèle commun où le code d'initialisation du programme définit un singleton, qui est ensuite lu uniquement par les threads de travail.

Cependant, si tout fil jamais écrit à ces données tandis que d'autres en train de lire, vous devez le protéger d'une certaine façon. Si vous avez beaucoup de lecteurs et seulement l'écrivain occasionnel, il vaut la peine d'envisager une sorte de "lecture-écriture" verrouillage, qui permet à plusieurs lecteurs en l'absence de tous les écrivains.

+0

Vous n'avez pas besoin de verrouiller juste à cause de l'esprit. Vous avez seulement besoin d'un verrou si l'écriture est non atomique. Une écriture entière est atomique. Si quelque chose doit écrire 2 entiers pour maintenir un état cohérent, alors vous aurez besoin d'un verrou pour vous assurer que les deux sont mis à jour avant que quiconque ne lise l'objet. –

+0

Il n'est pas garanti que l'affectation Int soit atomique ... – user23167

1

Vos méthodes sont correctes, mais vous avez deux problèmes majeurs.

Tout d'abord, MyData :: getInstance() doit être statique et ne crée pas d'instance. Afin de minimiser l'utilisation de mutex, vous devriez vérifier le double checked lock. Deuxièmement, la sécurité des threads dépend des appelants des méthodes getter et non de la classe MyData. Si c'est ce que vous cherchez, alors génial. Si ce n'est pas le cas, vous devrez créer un contrôle d'accès dans MyClass. De plus, puisque votre type de données contenues est juste un type de base (int), il y a de fortes chances pour que vous ne voyiez aucun problème de synchronisation jusqu'à ce que votre code soit en production.

+0

N'utilisez JAMAIS un casier à double vérification. Il ne peut pas être fait pour travailler en C++. Voir http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf –

+0

Je ne suis pas d'accord avec le fait qu'une double vérification ne devrait jamais être utilisée, même si je comprends et suis d'accord avec l'analyse de Meyers et Alexandrescu WRT la norme. En pratique, la DCL est une solution quasi-parfaite au problème "mon principal() thread doit initialiser les choses et d'autres threads les utilisent". – Clay

2

Il n'est pas possible de dire si cela est threadsafe. Si les données sont initialisées lors de la création de l'objet et ne changent jamais, cela fonctionnera correctement. Si vous mutez les données sous-jacentes par d'autres méthodes, alors les lecteurs devront effectuer une sorte de synchronisation contre les auteurs, il n'y a pas moyen de contourner cela. En fonction de ce que vous faites exactement, vous pouvez utiliser un poids plus léger qu'un Mutex, comme des mises à jour atomiques synchroniser des commandes, ou utiliser des verrous de lecteur-graveur, mais sans en savoir plus sur ce que vous faites, c'est impossible. dire.

1

d'abord revenir en arrière et lire cette question pour obtenir une meilleure version de Singelton:
Can any one provide me a sample of Singleton in c++?

Notez également: Est-ce que PAS utiliser un singleton comme une variable globale glorifié.
Cela ajoute simplement de la complexité à un mauvais design. Utilisez simplement une variable globale.

Tant que vous lisez uniquement à partir du singleton, il est thread-safe lorsqu'il est utilisé.

Le seul point qui n'est pas sûr pour les threads (non garanti par la langue) est lors de la création. Donc, techniquement, vous devriez ajouter des verrous autour de la partie qui crée l'instance pour garantir que singleton a été entièrement créé avant que quiconque puisse l'utiliser.

Note: Ne vous laissez pas attirer par un double verrouillage pour optimiser votre stratégie de verrouillage. Il peut pas être fait pour fonctionner correctement en C++. Lisez ceci DDJ article. Si vous pouvez garantir que le singleton est instancié (probablement le premier appel de getInstance()) dans un environnement à thread unique (avant que les threads ne soient créés), vous pouvez vous passer du besoin de verrous pendant l'instanciation.

Si vous modifiez votre code afin que d'autres threads puissent écrire dans le singleton, vous devez penser au verrouillage et à la cohérence. Vous avez seulement besoin de verrouillage si vos écritures ne sont pas atomiques. Si votre écriture ne fait que mettre à jour un entier, il est déjà atomique, aucun changement n'est requis. Si votre code n'est pas atomique:

  • écrire à plusieurs entiers dans une méthode
  • Exécution d'une lecture et d'écriture

Ensuite, vous devez verrouiller l'objet jusqu'à ce que toutes les écritures sont complets et par conséquent avoir des verrous sur les méthodes qui ont un accès en lecture pour les empêcher de lire depuis l'objet tandis qu'une autre méthode met à jour l'objet.

Par conséquent, ceci est une autre raison pour laquelle les variables membres ne doivent être accessibles que par des méthodes membres.

0

Pour la sécurité des filetages, vous devez considérer la classe dans son ensemble. Comme écrit votre classe ne serait pas thread safe. Bien que votre méthode getValues ​​soit correcte, la méthode getData1 rencontre des problèmes.

Vous dites qu'il s'agit de méthodes getter (en lecture seule). Cependant, aucune n'est déclarée comme méthode const. GetData1 ne serait pas valide en tant que méthode const car il renvoie un pointeur non constant. En outre, renvoyer un pointeur vers des données de classe privée est incorrect lorsque vous exposez votre implémentation.

S'il s'agit d'une classe singleton pour contenir un jeu de données essentiellement statique à l'initialisation avant le lancement de votre thread, tous vos accesseurs doivent être des méthodes const. GetInstance doit également renvoyer un pointeur const à la classe (et être une méthode statique comme mentionné par une autre réponse).

Questions connexes