2013-05-20 11 views
17

Diciamo che ho un puntatore a un numero intero.Il puntatore dereferenziazione è atomico?

volatile int* commonPointer = new int(); 

E ho più thread che il riferimento a tale puntatore.

int blah = *commonPointer; 

Ma, un thread ha bisogno di cambiare indirizzo del puntatore:

int* temp = new int(); 
int* old = commonPointer; 
InterlockedExchange(&commonPointer,temp); 
delete old; 

Ora, consente di ignorare il fatto che alcuni thread possono essere la lettura del valore "vecchio", e alcuni possono essere la lettura del " nuovo "valore, non è un problema nel mio caso.

Può esserci uno scenario in cui un thread inizia a dereferenziare il puntatore, proprio come l'indirizzo viene eliminato, e quindi ottiene un'eccezione?
Oppure il dereferenziamento è abbastanza atomico da non riuscire?

+5

"atomico * sufficiente *"? Non è garantito che la lettura di un intero a 32 bit sia atomica secondo lo standard definito dalla piattaforma. In pratica di solito lo sarà, ma puoi sempre controllarlo tu stesso. –

+0

Qual è l'architettura del sistema operativo e della CPU? –

+0

@ Eds. La lettura di un intero a 32 bit è atomica. Ma il dereferenziamento del puntatore ... non ne sono così sicuro. È più di una lettura ... legge l'indirizzo a 32 bit (o 64 in x64) del puntatore e quindi deve fare un'altra lettura da quell'indirizzo. –

risposta

22

Nulla nello standard C++ garantisce l'atomicità in questo caso.

È necessario proteggere le aree di codice rilevanti con un mutex: anche std::atomic non è sufficiente poiché fornirebbe solo l'accesso atomico al puntatore ma non includerebbe l'operazione di dereferenziazione.

+0

std :: stomic garantirà il valore della variabile atomica. ma non garantisce il dereferenziamento di quel valore. –

+4

@YochaiTimmer: non è esattamente quello che ho detto? ;) – syam

+1

Sì, non so perché non l'ho notato :) –

1

Edit2: Siamo spiacenti, no, non sarà di aiuto. Hai bisogno di mutex per l'accesso - è possibile (molto probabilmente) che il codice generato dal compilatore carichi il puntatore in un registro [o altra memoria, come una pila, se si tratta di un processore senza registri], quindi accede alla memoria dei punti del puntatore a, e allo stesso tempo, il puntatore viene aggiornato da un altro thread. L'unico modo per garantire che il puntatore sia corretto consiste nell'usare mutex o costrutti simili per chiudere l'intero blocco dell'accesso. Tutto il resto è possibile fallire.

Come dice syam, lo standard non garantisce che anche la lettura di un valore a 32 bit a cui punta il puntatore del mouse sia atomica, dipende dall'implementazione del sistema. Tuttavia, se stai chiedendo "otterrò un valore che è il vecchio o il nuovo valore", allora almeno x86 e x86-64 lo garantiranno. Altre architetture di macchina potrebbero non esserlo (un'implementazione int a 32 bit su un processore SMP 68000 non lo garantirebbe, poiché le scritture sono 16 bit alla volta e il secondo processore potrebbe averne scritto metà, ma non l'altra - non quello Sono a conoscenza di un sistema SMP con 68000 processori mai costruiti).

Il InterlockedExchange (che non è una funzione "standard") garantirà che il processore di questo thread abbia accesso ESCLUSIVO al puntatore stesso, quindi sarà sicuro di farlo - nessun altro processore sarà in grado di accedere al puntatore in quel punto . Questo è l'intero punto delle istruzioni "bloccate" nell'architettura x86: sono "sicure" (e abbastanza lente, ma assumendo che non lo faccia ogni volta ...).

Modifica: Si noti che è necessario fare attenzione con commonPointer stesso, perché il compilatore potrebbe non rendersi conto che si sta utilizzando un altro thread per aggiornarlo. Quindi potresti ancora leggere dal valore del puntatore OLD.

Una chiamata a una funzione [che non è stata declinata al nulla] o la dichiarazione del puntatore volatile int * volatile commonPointer; dovrebbe fare il trucco. [metto in discussione la mia risposta per aver usato volatile, dal momento che "non vi è alcun problema per cui la soluzione sia volatile come qualcuno ha postato in precedenza].

[Vedi EDIT2 sopra]

+0

Anche i due "volatili" non saranno sufficienti, anche con il significato esteso di Microsoft di "volatile" (che non è implementato in tutti i compilatori , né con tutte le opzioni del compilatore). 'volatile' è completamente irrilevante qui, in generale. –

3

In primo luogo, il volatile nella dichiarazione non ha alcun effetto reale . E in secondo luogo, non appena si modifica un valore in un thread e lo si accede in più thread, è necessario proteggere tutti gli accessi . Altrimenti, hai un comportamento indefinito. Non so quali garanzie fornisce InterlockedExchange, ma sono sicuro che non ha alcun effetto su nessuno dei thread che non lo chiamano.

+0

InterlockedExchange garantisce che il valore nel puntatore venga aggiornato atomicamente su tutte le CPU. Ma come discusso nella mia risposta, non aiuta, perché abbiamo ancora una corsa tra il valore del puntatore e il valore di riferimento che viene letto. Si prega di vedere Edit2 nella parte superiore del mio post. –

4

Forse, C++ 11

atomic<shared_ptr<int> > 

adattano alle vostre esigenze. Impedisce il vecchio valore di scomparire fino a quando almeno un riferimento al valore è valido.

atomic<shared_ptr<int> > commonPointer; 

// producer: 
{ 
    shared_ptr<int> temp(new int); 
    shared_ptr<int> old= atomic_exchange(&commonPointer, temp); 
    //... 
};// destructor of "old" decrements reference counter for the old value 


// reader: 
{ 
    shared_ptr<int> current= atomic_load(&commonPointer); 

    // the referent pointed by current will not be deleted 
    // until current is alive (not destructed); 
} 

Tuttavia, bloccare implementazione libera di PTR condiviso atomica è abbastanza complicato in modo probabilmente serrature o serrature di spin saranno utilizzati all'interno l'attuazione biblioteca (anche se l'implementazione è disponibile sulla piattaforma).

+0

Bella soluzione che utilizza materiale standard. Ma ci sarebbero almeno 2 lucchetti lì, uno per l'atomico e uno per l'shared_ptr. Meglio creare un singolo blocco personale. –

+0

I puntatori condivisi atomici possono essere implementati utilizzando i puntatori di emergenza. Anche se è complicato, può essere implementato senza blocchi, –

Problemi correlati