2013-07-26 13 views
5

Ho più thread che accedono a una singola variabile Int32 con l'operazione "++" o "-".L'operazione ++ è atomica in C#?

Abbiamo bisogno di bloccare prima di accedervi come di seguito?

lock (idleAgentsLock) 
    { 
     idleAgents--; //we should place it here. 
    } 

O quali conseguenze ci saranno se non faccio il blocco?

+1

La domanda migliore è probabile, è ciò che * utilizza * la concorrenza in idleAgents? – user2246674

+0

possibile duplicato di [Il thread dell'operatore ++ è sicuro?] (Http://stackoverflow.com/questions/4628243/is-the-operator-thread-safe) – nawfal

risposta

6

Non è "atomico", non nel senso multithread. Tuttavia il tuo lucchetto protegge l'operazione, almeno in teoria. In caso di dubbi, è possibile utilizzare lo Interlocked.Decrement.

1

Dipende dall'architettura della macchina. In generale, no, il compilatore può generare un'istruzione di caricamento, incrementare/decrementare il valore e quindi memorizzarlo. Quindi, altri thread potrebbero effettivamente leggere il valore tra quelle operazioni.

La maggior parte dei set di istruzioni della CPU ha uno speciale test atomico & impostato per questo scopo. Supponendo che non si vogliano incorporare le istruzioni di assemblaggio nel codice C#, il prossimo approccio migliore consiste nell'utilizzare l'esclusione reciproca, in modo simile a ciò che si è mostrato. L'implementazione di quel meccanismo utilizza in ultima analisi un'istruzione che è atomica per implementare il mutex (o qualsiasi altra cosa usi).

In breve: sì, è necessario garantire l'esclusione reciproca.

Oltre allo scopo di questa risposta, esistono altre tecniche per la gestione di dati condivisi che potrebbero essere appropriati o meno in base alla logica di dominio della situazione.

1

A non protetto incremento/decremento non è thread-safe - e quindi non atomico tra i thread. (Anche se potrebbe essere "atomica" wrt l'effettiva IL/ML trasformare .)

Questo codice di esempio LINQPad mostra risultati imprevedibili:

void Main() 
{ 
    int nWorkers = 10; 
    int nLoad = 200000; 
    int counter = nWorkers * nLoad; 
    List<Thread> threads = new List<Thread>(); 
    for (var i = 0; i < nWorkers; i++) { 
     var th = new Thread((_) => { 
      for (var j = 0; j < nLoad; j++) { 
       counter--; // bad 
      } 
     }); 
     th.Start(); 
     threads.Add(th); 
    } 
    foreach (var w in threads) { 
     w.Join(); 
    } 
    counter.Dump(); 
} 

noti che la visibilità tra i thread è di importanza . La sincronizzazione garantisce questa visibilità oltre all'atomicità.

Questo codice è facilmente risolvibile, almeno nel contesto limitato presentato.Passare il decremento e osservare i risultati:

counter--;       // bad 
Interlocked.Decrement(ref counter); // good 
lock (threads) { counter--; }  // good 

Anche quando si utilizza una variabile volatile, i risultati sono ancora imprevedibili. Ciò sembra indicare che (almeno qui, quando l'ho appena eseguito) è anche non un operatore atomico come lettura/op/scrittura di thread in competizione erano interlacciati. Per vedere che il comportamento non è ancora corretta quando vengono rimossi problemi di visibilità (sono?), Aggiungere

class x { 
    public static volatile int counter; 
} 

e modificare il codice qui sopra per usare x.counter al posto della variabile locale counter.

Problemi correlati