In pratica ho una variabile contatore decrementata e frequentemente sottoposta a polling. Ci sono tre opzioni per implementarlo, che posso pensare:
Numero 1:
private volatile int count;
public void Finished() {
Interlocked.Decrement(ref count);
}
public bool Done() {
return count == 0;
}
Numero 2:
private int count;
public void Finished() {
Interlocked.Decrement(ref count);
}
public bool Done() {
return Volatile.Read(ref count) == 0;
}
Numero 3:
struct AtomicInteger {
private int value;
public int Get() {
return Volatile.Read(ref value);
}
// other methods
}
// In actual class:
private AtomicInteger count;
public bool Done() {
return count.Get() == 0;
}
Il primo fornisce un avvertimento del compilatore a causa del passaggio di una variabile volatile per riferimento. D'altra parte è chiaro dalla variabile definizione che la variabile è accessibile contemporaneamente che è chiaramente un segnale di avvertimento per chiunque stia guardando il codice. Se mai dovessi decidere di avere effettivamente bisogno di molto, questo metodo non funziona più.
Il secondo non dà alcun avvertimento, ma solo leggendo private int count
non è chiaro che la variabile sia usata contemporaneamente, quindi questo deve essere esplicitamente indicato con un commento (in realtà avevo un bug in cui ho usato un lettura normale in un'asserzione anziché Volatile.Read
- una cosa così semplice da trascurare.)
Cambiare la variabile su un valore long invece non introduce alcun lavoro aggiuntivo.
Mi piace il numero 3 e non dovrebbe comportare un sovraccarico delle prestazioni rispetto alle altre soluzioni dopo l'inline. Lato negativo: duplicazione del codice per ogni primitivo. D'altra parte il codice è abbastanza semplice e può essere copiato senza timore di problemi ed è improbabile che cambi mai.
Qualche commento sui vantaggi / svantaggi di ciascun metodo che ho trascurato o che cosa sarebbe considerato idiomatico in C #?