Domanda

Ho un oggetto per il quale vorrei tenere traccia del numero di thread che lo fanno riferimento. In generale, quando viene chiamato un metodo sull'oggetto, posso verificare un valore booleano locale del thread per determinare se il conteggio è stato aggiornato per il thread corrente. Ma questo non mi aiuta se l'utente dice, usa boost :: bind per associare il mio oggetto a una funzione boost :: e lo usa per avviare un boost :: thread. Il nuovo thread avrà un riferimento al mio oggetto e potrebbe trattenerlo per un periodo di tempo indefinito prima di chiamare uno dei suoi metodi, portando quindi a un conteggio non aggiornato. Potrei scrivere il mio wrapper su boost :: thread per gestirlo, ma ciò non aiuta se l'utente boost :: bind è un oggetto che contiene il mio oggetto (non posso specializzarmi in base al presenza di un tipo di membro - almeno non conosco alcun modo per farlo) e lo uso per avviare un boost :: thread.

C'è un modo per farlo? L'unico mezzo a cui riesco a pensare richiede troppo lavoro da parte degli utenti: fornisco un wrapper attorno a boost :: thread che chiama un metodo di hook speciale sull'oggetto che viene passato purché esista e gli utenti aggiungono il metodo di hook speciale a qualsiasi classe che contiene il mio oggetto.

Modifica: per motivi di questa domanda possiamo supporre che io controlli i mezzi per creare nuovi thread. Quindi posso avvolgere boost :: thread per esempio e aspettarmi che gli utenti utilizzino la mia versione wrapped e non debbano preoccuparsi degli utenti che usano contemporaneamente pthreads, ecc.

Modifica2: si può anche supporre che io abbia alcuni mezzi di archiviazione locale thread disponibili, tramite __thread o boost::thread_specific_ptr. Non è nello standard attuale, ma si spera lo sarà presto.

È stato utile?

Soluzione

In generale, questo è difficile. La domanda di & Quot; chi ha un riferimento a me? & Quot; non è generalmente risolvibile in C ++. Potrebbe valere la pena guardare il quadro generale del problema o dei problemi specifici che stai cercando di risolvere e vedere se esiste un modo migliore.

Ci sono alcune cose che posso escogitare che possono portarti a metà strada lì, ma nessuno di loro è proprio quello che vuoi.

Puoi stabilire il concetto di " il thread proprietario " per un oggetto e le operazioni REJECT da qualsiasi altro thread, a la Qt GUI elements. (Tieni presente che il tentativo di eseguire operazioni in modalità thread-safe da thread diversi dal proprietario in realtà non ti garantirà la sicurezza del thread, poiché se il proprietario non è controllato può scontrarsi con altri thread.) Questo almeno dà agli utenti errori- comportamento veloce.

Puoi incoraggiare il conteggio dei riferimenti facendo in modo che gli oggetti visibili all'utente siano leggeri riferimenti all'oggetto di implementazione stesso [e documentandolo!]. Ma determinati utenti possono aggirare il problema.

E puoi combinare questi due-- cioè puoi avere la nozione di proprietà del thread per ogni riferimento , e quindi l'oggetto diventa consapevole di chi possiede i riferimenti. Questo potrebbe essere molto potente, ma non proprio a prova di idiota.

Puoi iniziare a limitare ciò che gli utenti possono e non possono fare con l'oggetto, ma non penso che valga la pena coprire più delle ovvie fonti di errore involontario. Dovresti dichiarare l'operatore & Amp; privato, quindi le persone non possono prendere puntatori ai tuoi oggetti? Dovresti impedire alle persone di allocare dinamicamente il tuo oggetto? Dipende dai tuoi utenti in una certa misura, ma tieni presente che non puoi impedire riferimenti agli oggetti, quindi alla fine giocare a Whack-a-Mole ti farà impazzire.

Quindi, tornando al mio suggerimento originale: riesaminare il quadro generale, se possibile.

Altri suggerimenti

A corto di un'implementazione in stile pimpl che fa un controllo threadid prima di ogni dereference, non vedo come potresti farlo:

 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();
         }
     }
 };

Nota: so che l'OP vuole elencare i thread con puntatori attivi, non credo sia fattibile. L'implementazione di cui sopra consente almeno all'oggetto di sapere quando potrebbe esserci contesa. Quando cambiare owning_thread dipende fortemente da cosa fa qualcosa.

Di solito non è possibile farlo a livello di codice.

Sfortunatamente, la strada da percorrere è progettare il tuo programma in modo tale da poter provare (cioè convincerti) che alcuni oggetti sono condivisi e altri sono thread privati.

L'attuale standard C ++ non ha nemmeno la nozione di thread, quindi non esiste una nozione portatile standard di archiviazione locale dei thread, in particolare.

Se ho capito correttamente il tuo problema, credo che questo potrebbe essere fatto in Windows usando la funzione Win32 GetCurrentThreadId () . Di seguito è riportato un esempio rapido e sporco di come potrebbe essere utilizzato. La sincronizzazione dei thread dovrebbe piuttosto essere eseguita con un oggetto lock.

Se si crea un oggetto di CMyThreadTracker nella parte superiore di ogni funzione membro dell'oggetto da tenere traccia dei thread, _handle_vector deve contenere gli ID thread che utilizzano l'oggetto.

#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: Come menzionato nel commento, l'erogazione di riferimento potrebbe essere implementata come di seguito. Un vettore potrebbe contenere gli ID thread univoci che si riferiscono al tuo oggetto. Potrebbe inoltre essere necessario implementare un operatore di assegnazione personalizzato per gestire i riferimenti agli oggetti copiati da un thread diverso.

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 soluzione con cui ho familiarità è dichiarare " se non si utilizza l'API corretta per interagire con questo oggetto, tutte le scommesse sono disattivate. "

Potresti essere in grado di invertire le tue esigenze e renderlo possibile per tutti i thread che fanno riferimento all'oggetto iscriviti ai segnali dall'oggetto . Questo non aiuta con le condizioni di gara, ma consente ai thread di sapere quando l'oggetto si è scaricato da solo (ad esempio).

Per risolvere il problema " Ho un oggetto e voglio sapere quanti thread vi accedono " e puoi anche enumerare i tuoi thread, puoi risolvere questo problema con l'archiviazione locale dei thread. Allocare un indice TLS per il tuo oggetto. Crea un metodo privato chiamato & Quot; registerThread & Quot; che imposta semplicemente il thread TLS in modo che punti al tuo oggetto.

L'estensione chiave dell'idea originale del poster è che durante ogni chiamata di metodo, chiama questo registerThread (). Quindi non è necessario rilevare quando o chi ha creato il thread, è impostato (spesso in modo ridondante) durante ogni accesso effettivo.

Per vedere quali thread hanno avuto accesso all'oggetto, basta esaminare i loro valori TLS.

Upside: semplice e piuttosto efficiente.

Unico inconveniente: risolve la domanda pubblicata ma non si estende uniformemente a più oggetti o thread dinamici che non sono enumerabili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top