Question

J'ai un objet pour lequel j'aimerais suivre le nombre de threads qui le référencent. En général, quand une méthode de l'objet est appelée, je peux vérifier la valeur booléenne locale d'un thread pour déterminer si le compte a été mis à jour pour le thread actuel. Mais cela ne m'aide pas si l'utilisateur dit, utilise boost :: bind pour lier mon objet à une fonction boost :: et l'utilise pour démarrer un boost :: thread. Le nouveau fil fera référence à mon objet et pourra le conserver pendant une durée indéterminée avant d'appeler l'une de ses méthodes, ce qui conduira à un compte obsolète. Je pourrais écrire mon propre wrapper autour de boost :: thread pour gérer cela, mais cela n’aide en rien si l’utilisateur boost :: bind est un objet qui contient mon objet (je ne peux pas me spécialiser en fonction de la présence d'un type de membre - du moins, je ne connais aucun moyen de le faire) et l'utilise pour démarrer un boost :: thread.

Y a-t-il un moyen de faire cela? Le seul moyen auquel je peux penser nécessite trop de travail de la part des utilisateurs - je fournis un wrapper autour de boost :: thread qui appelle une méthode de hook spéciale sur l'objet transmis, à condition qu'il existe, et les utilisateurs ajoutent cette méthode à n'importe quelle classe qui contient mon objet.

Edit: Pour cette question, nous pouvons supposer que je contrôle les moyens de créer de nouveaux threads. Ainsi, je peux envelopper boost :: thread par exemple et m'attendre à ce que les utilisateurs utilisent ma version intégrée, sans avoir à s'inquiéter de l'utilisation simultanée de pthreads, etc. par les utilisateurs.

Edit2: On peut également supposer que je dispose de certains moyens de stockage local de threads via __thread ou boost::thread_specific_ptr. Ce n'est pas dans la norme actuelle, mais j'espère que ce sera bientôt.

Était-ce utile?

La solution

En général, c'est difficile. La question de & "Qui a une référence à moi? &"; n'est généralement pas soluble en C ++. Cela vaut peut-être la peine d’avoir une vue d’ensemble du (des) problème (s) spécifique (s) que vous essayez de résoudre et de voir s’il existe une meilleure solution.

Il y a quelques choses que je peux trouver qui peuvent vous amener à mi-chemin, mais aucune d'entre elles n'est tout à fait ce que vous voulez.

Vous pouvez établir le concept de " le thread propriétaire " pour un objet et les opérations REJECT de tout autre thread, comme les éléments de l'interface graphique Qt. (Notez qu'essayer de faire des choses en toute sécurité avec des threads autres que le propriétaire ne vous assurera pas réellement la sécurité des threads, car si le propriétaire n'est pas coché, il peut entrer en collision avec d'autres threads.) Cela donne au moins à vos utilisateurs un échec. comportement rapide.

Vous pouvez encourager le comptage des références en faisant en sorte que les objets visibles par l'utilisateur constituent des références simples à l'objet d'implémentation lui-même [et en documentant ceci!]. Mais des utilisateurs déterminés peuvent contourner ce problème.

Et vous pouvez combiner ces deux options, c’est-à-dire que vous pouvez avoir la notion de propriété de fil pour chaque référence , puis que l’objet devienne conscient du propriétaire des références. Cela pourrait être très puissant, mais pas vraiment idiot.

Vous pouvez commencer à restreindre ce que les utilisateurs peuvent et ne peuvent pas faire avec l'objet, mais je ne pense pas qu'il vaille la peine de couvrir davantage que les sources évidentes d'erreurs non intentionnelles. Si vous déclarez l'opérateur & Amp; privé, de sorte que les gens ne peuvent pas prendre des pointeurs sur vos objets? Devriez-vous empêcher les gens d'allouer dynamiquement votre objet? Cela dépend de vos utilisateurs dans une certaine mesure, mais gardez à l'esprit que vous ne pouvez pas empêcher les références à des objets. Par conséquent, jouer à whack-a-mole vous rendra fou.

Revenons donc à ma suggestion initiale: analysez à nouveau la vue d’ensemble, si possible.

Autres conseils

En dehors d'une implémentation de style pimpl qui effectue une vérification d'ID de thread avant chaque déréférence, je ne vois pas comment vous pourriez le faire:

 class MyClass;
 class MyClassImpl {
     friend class MyClass;
     threadid_t owning_thread;
 public:
     void doSomethingThreadSafe();
     void doSomethingNoSafetyCheck();
 };

 class MyClass {
     MyClassImpl* impl;
 public:
     void doSomethine() {
         if (__threadid() != impl->owning_thread) {
             impl->doSomethingThreadSafe();
         } else {
             impl->doSomethingNoSafetyCheck();
         }
     }
 };

Remarque: je sais que le PO veut lister les discussions avec des pointeurs actifs, je ne pense pas que ce soit faisable. L'implémentation ci-dessus permet au moins à l'objet de savoir quand il peut y avoir conflit. Quand changer le fils de owning_thread dépend fortement de ce que faitQuelque chose fait.

Généralement, vous ne pouvez pas le faire par programmation.

Malheureusement, la meilleure chose à faire est de concevoir votre programme de manière à pouvoir prouver (c'est-à-dire vous convaincre vous-même) que certains objets sont partagés et que d'autres sont des threads privés.

Le standard C ++ actuel n'a même pas la notion de thread. Il n'y a donc pas de notion portable standard de stockage local de thread, en particulier.

Si j'ai bien compris votre problème, j'estime que cela pourrait être fait sous Windows à l'aide de la fonction Win32 GetCurrentThreadId () . Vous trouverez ci-dessous un exemple rapide et peu explicite d'utilisation. La synchronisation des threads devrait plutôt être effectuée avec un objet verrou.

Si vous créez un objet de CMyThreadTracker en haut de chaque fonction membre de votre objet à suivre pour les threads, le _handle_vector doit contenir les identifiants de threads utilisant votre objet.

#include <process.h>
#include <windows.h>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;


class CMyThreadTracker
{

    vector<DWORD> & _handle_vector;
    DWORD _h;
    CRITICAL_SECTION &_CriticalSection;
public:
    CMyThreadTracker(vector<DWORD> & handle_vector,CRITICAL_SECTION &crit):_handle_vector(handle_vector),_CriticalSection(crit)
    {
        EnterCriticalSection(&_CriticalSection); 
        _h = GetCurrentThreadId();
        _handle_vector.push_back(_h);
        printf("thread id %08x\n",_h);
        LeaveCriticalSection(&_CriticalSection);
    }

    ~CMyThreadTracker()
    {
        EnterCriticalSection(&_CriticalSection); 
        vector<DWORD>::iterator ee = remove_if(_handle_vector.begin(),_handle_vector.end(),bind2nd(equal_to<DWORD>(), _h));
        _handle_vector.erase(ee,_handle_vector.end());
        LeaveCriticalSection(&_CriticalSection);
    }
};

class CMyObject
{
    vector<DWORD> _handle_vector;

public:
    void method1(CRITICAL_SECTION & CriticalSection)
    {
        CMyThreadTracker tt(_handle_vector,CriticalSection);

        printf("method 1\n");

        EnterCriticalSection(&CriticalSection);
        for(int i=0;i<_handle_vector.size();++i)
        {
            printf(" this object is currently used by thread %08x\n",_handle_vector[i]);
        }
        LeaveCriticalSection(&CriticalSection);

    }
};

CMyObject mo;
CRITICAL_SECTION CriticalSection;

unsigned __stdcall ThreadFunc( void* arg )
{

    unsigned int sleep_time = *(unsigned int*)arg;
    while ( true)
    {
        Sleep(sleep_time);
        mo.method1(CriticalSection);
    }

    _endthreadex( 0 );
    return 0;
} 

int _tmain(int argc, _TCHAR* argv[])
{

    HANDLE hThread;
    unsigned int threadID;

    if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) ) 
        return -1;

    for(int i=0;i<5;++i)
    {
        unsigned int sleep_time = 1000 *(i+1);

        hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunc, &sleep_time, 0, &threadID );
        printf("creating thread %08x\n",threadID);
    }

    WaitForSingleObject( hThread, INFINITE );

    return 0;
}

EDIT1: Comme indiqué dans le commentaire, la distribution des références pourrait être mise en œuvre comme indiqué ci-dessous. Un vecteur peut contenir les identifiants de thread uniques faisant référence à votre objet. Vous devrez peut-être également implémenter un opérateur d’affectation personnalisé pour gérer les références d’objet en cours de copie par un autre thread.

class MyClass
{
public:
static MyClass & Create()
    {
    static MyClass * p = new MyClass();

    return *p;
    }
    static void Destroy(MyClass * p)
    {
        delete p;
    }

private:
    MyClass(){}
    ~MyClass(){};
};

class MyCreatorClass
{
    MyClass & _my_obj;
public:
MyCreatorClass():_my_obj(MyClass::Create())
    {

    }

    MyClass & GetObject()
    {
        //TODO: 
        // use GetCurrentThreadId to get thread id
        // check if the id is already in the vector
        // add this to a vector

        return _my_obj;
    }

    ~MyCreatorClass()
    {
        MyClass::Destroy(&_my_obj);
    }

}; 

int _tmain(int argc, _TCHAR* argv[])
{
    MyCreatorClass mcc;

    MyClass &o1 = mcc.GetObject();

    MyClass &o2 = mcc.GetObject();

    return 0;
}

La solution que je connais bien consiste à indiquer & "si vous n'utilisez pas la bonne API pour interagir avec cet objet, tous les paris sont alors coupés. &";

Vous pourrez peut-être modifier vos exigences et rendre possible l’utilisation de tout fil qui référence l’objet abonnez-vous aux signaux de l'objet . Cela n’aidera pas dans les conditions de concurrence, mais permettra aux threads de savoir quand l’objet s’est déchargé (par exemple).

Pour résoudre le problème & "J'ai un objet et je veux savoir combien de threads y ont accès &"; et vous pouvez aussi énumérer vos threads, vous pouvez résoudre ce problème avec le stockage local des threads. Allouez un index TLS pour votre objet. Créez une méthode privée appelée & Quot; registerThread & Quot; qui définit simplement le thread TLS pour qu'il pointe vers votre objet.

L'idée clé de l'affiche est l'extension suivante: lors de tous les appels de méthodes , appelez cette méthode registerThread (). Ensuite, vous n'avez pas besoin de détecter quand ou qui a créé le fil de discussion, c'est simplement défini (souvent de manière redondante) lors de chaque accès réel.

Pour voir quels threads ont accédé à l'objet, il suffit d'examiner leurs valeurs TLS.

Upside: simple et assez efficace.

Inconvénient: résout la question publiée mais ne s'étend pas facilement à plusieurs objets ou threads dynamiques non énumérables.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top