Il seguente codice è uno scheletro di una classe di puntatore atomico prelevata da un'applicazione di ricottura simulata nello PARSEC benchmark suite for shared-memory multiprocessors.Scambio atomico di due oggetti std :: atomici <T*> in modalità senza blocco in C++ 11?
In tale applicazione, la struttura dati centrale è un grafico (più specificamente una netlist di un circuito integrato). Ogni nodo nel grafico ha un attributo che indica la sua posizione fisica. L'algoritmo genera più thread e ogni thread ripetutamente e seleziona in modo casuale due nodi e scambia le loro posizioni fisiche se ciò comporta un costo di routing migliore per il chip.
Poiché il grafico è enorme e ogni coppia di nodi può essere scelta da ciascun thread, l'unica soluzione praticabile è una struttura di dati concorrenti senza blocco (CDS). Ecco perché la seguente classe AtomicPtr
è cruciale (è usata per scambiare atomicamente puntatori con due oggetti di posizione fisica in modo bloccato). La funzione atomic_load_acq_ptr()
è definita nel codice assembly e corrisponde a std::atomic<T*>::load(memory_order_acquire)
.
Voglio implementare quel CDS usando gli atomici C++ 11.
template <typename T>
class AtomicPtr {
private:
typedef long unsigned int ATOMIC_TYPE;
T *p __attribute__ ((aligned (8)));
static const T *ATOMIC_NULL;
inline T *Get() const {
T *val;
do {
val = (T *)atomic_load_acq_ptr((ATOMIC_TYPE *)&p);
} while(val == ATOMIC_NULL);
return val;
}
inline void Swap(AtomicPtr<T> &X) {
// Define partial order in which to acquire elements to prevent deadlocks
AtomicPtr<T> *first;
AtomicPtr<T> *last;
// Always process elements from lower to higher memory addresses
if (this < &X) {
first = this;
last = &X;
} else {
first = &X;
last = this;
}
// Acquire and update elements in correct order
T *valFirst = first->Checkout(); // This sets p to ATOMIC_NULL so all Get() calls will spin.
T *valLast = last->PrivateSet(valFirst);
first->Checkin(valLast); // This restores p to valLast
}
};
Procedimento std::atomic<T*>::exchange()
può essere utilizzato solo per scambiare un puntatore nudo T*
con un oggetto std::atomic<T*>
. Come fare lo scambio di due oggetti std::atomic<T*>
in modo bloccato?
Quello che mi viene in mente è che la classe AtomicPtr
di seguito può essere a sua volta basata su std::atomic<T*>
dichiarando:
std::atomic<T*> p;
e sostituzione di tutte le chiamate da atomic_load_acq_ptr()
std::atomic<T*>::load(memory_order_acquire)
e sostituendo tutte le chiamate da atomic_store_rel_ptr()
std::atomic<T*>::store(memory_order_release)
. Ma il mio primo pensiero è che std::atomic<T*>
dovrebbe sostituire lo stesso AtomicPtr
e potrebbe esserci un modo intelligente per scambiare direttamente gli oggetti std::atomic<T*>
. qualche idea?
Non c'è modo di scambiare atomicamente il contenuto di due 'std :: atomic's in C++ 11. – Casey
in realtà, non penso che questo sia possibile anche su x86/x64 –
Non è possibile direttamente. Ma la classe 'AtomicPtr' lo fa già seguendo una check-out \ check-in: 1- Ogni thread che vuole fare uno scambio controlla prima il puntatore (scrivendo un valore sentinella chiamato' ATOMIC_NULL' sopra) e , al termine, controlla. 2- Qualsiasi thread che desideri leggere (cioè Get()), il valore del puntatore deve continuare a ruotare se il puntatore è stato estratto. –