L'assegnazione di riferimento è atomica, quindi perché è necessario interlocco.exchange (ref, oggetto)?

StackOverflow https://stackoverflow.com/questions/2192124

Domanda

Nel mio servizio Web ASMX multithread avevo un campo di classe _alldata del mio tipo di tipo di tipo di tipo. List<T> e Dictionary<T> contrassegnato come volatile. I dati del sistema (_allData) viene aggiornato di tanto in tanto e lo faccio creando un altro oggetto chiamato newData e riempire le sue strutture di dati con nuovi dati. Quando è fatto, ho appena assegnato

private static volatile SystemData _allData

public static bool LoadAllSystemData()
{
    SystemData newData = new SystemData();
    /* fill newData with up-to-date data*/
     ...
    _allData = newData.
} 

Questo dovrebbe funzionare poiché l'incarico è atomico e i thread che hanno il riferimento ai vecchi dati continuano a utilizzarli e il resto hanno i nuovi dati di sistema subito dopo l'assegnazione. Tuttavia il mio collega lo ha detto invece di usare volatile PAROLA CHIAVE E ASSIGMENTO SEMPLICE dovrei usare InterLocked.Exchange Perché ha detto che su alcune piattaforme non è garantito che l'assegnazione di riferimento sia atomico. Inoltre: quando dichiaro the _allData campo come volatile il

Interlocked.Exchange<SystemData>(ref _allData, newData); 

Produce avvertimento "un riferimento a un campo volatile non sarà trattato come volatile" Cosa dovrei pensare a questo?

È stato utile?

Soluzione

Ci sono numerose domande qui. Considerandoli uno alla volta:

L'assegnazione di riferimento è atomica, quindi perché è necessario interlocco.exchange (ref, oggetto)?

L'assegnazione di riferimento è atomica. Interlocked.Exchange non esegue solo l'assegnazione di riferimento. Fa una lettura del valore corrente di una variabile, distingue il vecchio valore e assegna il nuovo valore alla variabile, tutto come operazione atomica.

Il mio collega ha detto che su alcune piattaforme non è garantito che l'assegnazione di riferimento sia atomico. Il mio collega era corretto?

No. L'assegnazione di riferimento è garantita per essere atomica su tutte le piattaforme .NET.

Il mio collega sta ragionando da premesse false. Ciò significa che le loro conclusioni sono errate?

Non necessariamente. Il tuo collega potrebbe darti buoni consigli per cattive ragioni. Forse c'è qualche altra ragione per cui dovresti usare interblocco.exchange. La programmazione senza blocchi è follemente difficile e nel momento in cui ti allontani da pratiche consolidate sposate dagli esperti del campo, sei fuori dalle erbacce e rischiano il peggior tipo di condizioni di razza. Non sono né un esperto in questo campo né un esperto del tuo codice, quindi non posso esprimere un giudizio in un modo o nell'altro.

Produce avvertimento "un riferimento a un campo volatile non sarà trattato come volatile" Cosa dovrei pensare a questo?

Dovresti capire perché questo è un problema in generale. Ciò porterà a una comprensione del perché l'avvertimento non è importante in questo caso particolare.

Il motivo per cui il compilatore fornisce questo avvertimento è perché contrassegnare un campo come volatile significa "questo campo verrà aggiornato su più thread - non generare alcun codice che memorizza i valori di questo campo e assicurarsi che qualsiasi lettura o scrittura su Questo campo non è "spostato in avanti e indietro nel tempo" tramite incoerenze nella cache del processore ".

(Presumo che tu capisca già tutto ciò. Se non hai una comprensione dettagliata del significato di volatile e di come influisce sulla semantica della cache del processore, allora non capisci come funziona e non dovresti usare programmi volatili. sono molto difficili da avere ragione; assicurati che il tuo programma sia giusto perché capisci come funziona, non per caso.)

Supponiamo ora di creare una variabile che è un alias di un campo volatile passando un ref su quel campo. All'interno del metodo chiamato, il compilatore non ha alcun motivo per sapere che il riferimento deve avere una semantica volatile! Il compilatore genererà allegramente il codice per il metodo che non implementa le regole per i campi volatili, ma la variabile è un campo volatile. Ciò può distruggere completamente la tua logica senza blocchi; L'ipotesi è sempre che un campo volatile lo sia sempre Accesso con semantica volatile. Non ha senso trattarlo come volatile a volte e non altre volte; devi sempre Sii coerente altrimenti non è possibile garantire coerenza su altri accessi.

Pertanto, il compilatore avverte quando lo fai, perché probabilmente sta per incasinare completamente la logica senza blocchi attentamente sviluppata.

Naturalmente, interlocciato.Exchange è Scritto per aspettarsi un campo volatile e fare la cosa giusta. L'avvertimento è quindi fuorviante. Mi pento molto questo; Ciò che avremmo dovuto fare è implementare alcuni meccanismi in base al quale un autore di un metodo come Interlocod.Exchange potrebbe mettere un attributo sul metodo dicendo "Questo metodo che prende un ref applica la semantica volatile sulla variabile, quindi sopprimere l'avvertimento". Forse in una versione futura del compilatore lo faremo.

Altri suggerimenti

O il tuo collega si sbaglia o sa qualcosa che la specifica della lingua C# non è.

5.5 Atomicità di riferimenti variabili:

"Leggi e le scritture dei seguenti tipi di dati sono atomici: bool, char, byte, sbyte, corto, ushort, uint, int, float e tipi di riferimento."

Quindi, puoi scrivere sul riferimento volatile senza il rischio di ottenere un valore corrotto.

Ovviamente dovresti fare attenzione a come decidere quale thread dovrebbe recuperare i nuovi dati, per ridurre al minimo il rischio che più di un thread alla volta lo fa.

Interlocciato.Exchange <T>

Imposta una variabile del tipo T specificato su un valore specificato e restituisce il valore originale, come operazione atomica.

Cambia e restituisce il valore originale, è inutile perché vuoi solo cambiarlo e, come ha detto Guffa, è già atomico.

A meno che un profiler sia dimostrato che sia un collo di bottiglia nella tua domanda, dovresti considerare i blocchi di non singoli, è più facile da capire e dimostrare che il tuo codice è giusto.

Iterlocked.Exchange() non è solo atomico, si occupa anche della visibilità della memoria:

Le seguenti funzioni di sincronizzazione utilizzano gli ostacoli appropriati per garantire l'ordinamento della memoria:

Funzioni che entrano o lasciano sezioni critiche

Funzioni che segnalano la sincronizzazione oggetti

Funzioni di attesa

Funzioni interbloccate

Problemi di sincronizzazione e multiprocessore

Ciò significa che oltre all'atomicità assicura che:

  • Per il thread che lo chiama:
    • Nessun riordino delle istruzioni viene eseguito (dal compilatore, dal tempo di esecuzione o dall'hardware).
  • Per tutti i thread:
    • Nessuna lettura alla memoria che accade prima che questa istruzione vedrà la modifica fatta da questa istruzione.
    • Tutte le letture dopo questa istruzione vedranno la modifica apportata da questa istruzione.
    • Tutte le scritture in memoria dopo che questa istruzione avverranno dopo che questa modifica delle istruzioni ha raggiunto la memoria principale (eliminando questa modifica delle istruzioni alla memoria principale quando ha fatto e non lasciare che l'hardware sia il proprietario dei tempi).
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top