2012-10-04 16 views
7

Questo programma esegue due thread diversi e mi dice chi è il vincitore della "corsa".Confusione tra algoritmo di Dekker

Inaspettatamente a volte ENTRAMBI i thread "vince" (mi aspettavo che qualcuno o nessuno vincesse). È questo comportamento atteso e perché? Mi manca ovviamente qualcosa di fondamentale qui.

class Program 
{ 
    public volatile static int a = 0; 
    public volatile static int b = 0; 

    public static void Main() 
    { 
     for(int i = 0; i < 1000; i++) 
     { 
      a = 0; 
      b = 0; 

      Parallel.Invoke(delegate { a = 1; if (b == 0) Console.WriteLine("A wins"); }, 
          delegate { b = 1; if (a == 0) Console.WriteLine("B wins"); }); 

      Console.WriteLine(System.Environment.NewLine); 

      Thread.Sleep(500); 
     } 
    } 
} 

Risultati:

A wins 

B wins 

A wins 
B wins 

A wins 

... 
+0

Quando si modifica l'implementazione da 'Parallel' a povero' Thread's, sembra funzionare. (Non so perché ancora.) –

+0

@LB: Interessante, è probabilmente perché i thread vengono eseguiti sullo stesso processore- nucleo? Anche l'impostazione –

+0

'TaskCreationOptions.LongRunning' sembra funzionare correttamente. –

risposta

3

Stai utilizzando volatili in modo non corretto:

dichiarare le variabili volatili non è sufficiente, è necessario fare in modo che ovunque di leggere/scrivere loro, si utilizza Thread.VolatileRead(ref myVar)/Thread.VolatileWrite(ref myVar)

Inoltre, volatile fa NON garantisce l'ordine di lettura/scrittura (da diversi thread), anche se usato correttamente. Sfoglia SO per informazioni sull'argomento. EDIT: it seems to do su una singola macchina nucleo x86

si può semplicemente utilizzare l'istruzione lock, ma se si vuole andare a fondo di questo, Consiglierei la lettura, la comprensione, quindi la lettura di nuovo this free e-book

ADDITIONS:
Ho appena sfogliato la classe Parallel in .NET 4 e non viene utilizzata la parola chiave volatile.
Copiano inoltre l'array di Action<T> prima di ricollegarlo per qualche motivo, ma dubito che ciò ti abbia colpito.

+0

+1. Usando il filo.VolatileRead (ref myVar) ecc. In realtà fornisce il comportamento previsto. Le mie ipotesi sono (e quello che volevo vedere in primo luogo) che i risultati di non dichiarare le variabili come volatili (non inserendo le corrette barriere di memoria) danno un buon esempio del modello di memoria un po 'rilassato. Se vuoi approfondire ciò, sentiti libero. Probabilmente la contrassegnerò come accettata se non verrà fornita una spiegazione più dettagliata del PERCORSO. Grazie! –

+0

@ JK. leggi l'e-book gratuito per la spiegazione * dettagliata * (ma preparati!). –

+0

Ho letto l'ebook. Da quanto ho capito, VolatileRead aggiunge un MemoryBarrier. Tuttavia, ciò non garantisce che il secondo thread sarà in grado di leggere il valore "fresco" scritto dal primo thread. Quindi, anche se questo funziona, penso che teoricamente non dovrebbe (almeno usando i metodi VolatileRead e VolatieWrite). – Petrakeas

1

Sia Win quando si eseguono in parallelo.

Dalla documentazione (http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.invoke.aspx): Esegue ciascuna delle azioni fornite, eventualmente in parallelo.

+0

+1 buona cattura !!! – Gabber

+3

Questo è ancora strano. aeb sono volatili e vengono aggiornati prima di verificare il valore nell'altra attività. Ciò significa che nel momento in cui a sta controllando se b == 1, almeno deve essere 1 e viceversa, quindi sembra che logicamente al massimo si possa vincere. – GolezTrol

+0

@GolezTrol che avrebbe senso, ma anche se sono dichiarati come volatili, non vengono usati come tali: ottiene/imposta senza usare Volatile.Read/Volatile.Write –