2011-02-08 19 views
5

Ci sono tonnellate di domande su come implementare i contatori di riferimento thread-safe. E una risposta molto votata comune è: "usa incremento/decremento atomico". Ok, questo è un buon modo per leggere e scrivere refCounter mentre altri thread lo cambiano in mezzo. Ma.Un'altra domanda sul conteggio dei riferimenti sicuri dei thread

Il mio codice è:

void String::Release() 
{ 
    if (0 == AtomicDecrement(&refCounter))) 
     delete buffer; 
} 

So. Decremento e leggo refCounter in sicurezza. Ma cosa succede se l'altro thread INCREMENTO il mio RefCounter mentre lo sto confrontando a zero ????

Mi sbaglio?

EDIT: (esempio)

String* globalString = new String(); // refCount == 1 after that. 

// thread 0: 
delete globalString; 
    // This invokes String::Release(). 
    // After AtomicDecrement() counter becomes zero. 
    // Exactly after atomic decrement current thread switches to thread 1. 

// thread 1: 
String myCopy = *globalString; 
    // This invokes AddRef(); 
    // globalString is alive; 
    // internal buffer is still not deleted but refCounter is zero; 
    // We increment and switch back to thread 0 where buffer will be 
    // succefully deleted; 

mi sbaglio?

+2

Come può un altro thread incrementare il contatore, se non ha un riferimento all'oggetto ? Un valore di 0 significa letteralmente "nessun riferimento lasciato". –

risposta

1

Il tuo esempio mi sembra giusto.

Tuttavia, il problema qui non riguarda le operazioni atomiche, ma l'eliminazione manuale di un oggetto e quindi il riferimento a un oggetto che sta per essere eliminato. Cosa succede se il conteggio dei riferimenti, invece di essere 1, è 8 ?.

È necessario evitare di eliminare e invalidare manualmente l'oggetto e utilizzare in modo migliore l'implementazione di indicatori intelligenti di concorrenza per gestire il conteggio dei riferimenti.

Ogni volta che un puntatore rileva che il conto è zero, è necessario bloccare l'oggetto per evitare di essere referenziato da un altro thread, proprio come lo double-checked locking per inizializzare il nuovo riferimento.

+2

In che modo un altro thread accederà all'oggetto, quando non rimarrà alcun riferimento all'oggetto? –

+1

@Jeremy, come ha detto Anton nell'esempio di domanda, se un thread chiama il metodo Release() e, subito dopo la chiamata AtomicDecrement e prima dell'istruzione delete, un altro thread tenta di acquisire un riferimento, il valore del refcount sarebbe uguale a zero ma il secondo thread otterrebbe il riferimento, incrementando il conto prima dell'eliminazione, eliminando un oggetto con un conto == 1. È un caso altamente improbabile e non impossibile. – vz0

+0

Grazie persone. In realtà è un problema nel rendere globalString thread-safe e non il buffer interno. Quindi, come suggerito da vz0, dovrei bloccare il globalString prima di provare a lavorare con esso. Post scriptum String è solo un esempio, nel mio codice reale ho delle dipendenze un po 'più complicate. –

2

Attenzione!

Non è sufficiente proteggere una variabile come un contatore di riferimento che gestisce il ciclo di vita di qualcosa di più grande.

Ho visto il codice come quello nella sua domanda che finisce piuttosto male ...

Nel tuo caso non è solo che qualcuno possa incrementare il contatore dopo che il confronto, ma alcuni thread può ottenere il contatore con valore 1, allora si decrementa ed eliminare il buffer di memoria e l'altro uso filo cancellato ... CRASH

MY2C

Problemi correlati