2012-10-04 14 views
7

Ten program wykonać dwa różne wątki i powiedz mi, kto jest zwycięzcą "wyścigu".Wariant zamieszania Dekkera algorytm

Niespodziewanie czasami OBA wątki "wygrywa" (oczekiwałem, że ktoś lub nikt nie wygra). Czy to oczekiwane zachowanie i dlaczego? Najwyraźniej brakuje mi czegoś fundamentalnego.

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); 
     } 
    } 
} 

Wyniki:

A wins 

B wins 

A wins 
B wins 

A wins 

... 
+0

Podczas zmiany implementacji z 'Równoległego' na kiepski stary wątek, wydaje się, że działa. (Nie wiem dlaczego jeszcze.) –

+0

@LB: Interesujące, prawdopodobnie dlatego, że wątki działają na tym samym procesorze- rdzeń? –

+0

ustawienie 'TaskCreationOptions.LongRunning' również wydaje się, że działa poprawnie. –

Odpowiedz

3

Używasz lotny niepoprawnie:

deklarowania zmiennych lotny nie wystarczy, trzeba się upewnić, że wszędzie ty Odczyt/zapis ich użyć Thread.VolatileRead(ref myVar)/Thread.VolatileWrite(ref myVar)

Również lotny czy NOT zapewnia kolejność odczytu/zapisu (z różnych wątków), nawet jeśli jest używana poprawnie. Przeglądaj SO, aby uzyskać informacje na ten temat. EDIT: it seems to do na x86 jednej maszynie podstawowej

Mogłeś po prostu użyć instrukcji lock, ale jeśli chcesz, aby dotrzeć do sedna tego, polecam czytanie ze zrozumieniem, a potem czyta ponownie this free e-book

DODATKI:
Po prostu przejrzałem klasę Parallel w .NET 4 i nigdzie nie użyto słowa kluczowego volatile.
Kopiują także tablicę Action<T>, zanim z jakiegoś powodu ją zapętli, ale wątpię, żeby to wpłynęło na ciebie.

+0

+1. Za pomocą wątku.VolatileRead (ref myVar) itp. Faktycznie daje oczekiwane zachowanie. Moje przypuszczenia są (i to, co chciałem w pierwszej kolejności zobaczyć), że wyniki nie deklarowania zmiennych jako niestabilnych (bez wstawiania właściwych barier pamięciowych) stanowią dobry przykład w pewien sposób zrelaksowanego modelu pamięci. Jeśli chcesz to rozwinąć, poczuj się swobodnie. Prawdopodobnie oznaczę to jako zaakceptowane, jeśli nie podam bardziej szczegółowego wyjaśnienia DLACZEGO tak się dzieje. Dzięki! –

+0

@JK. przeczytaj darmowy e-book na * szczegółowe * wyjaśnienie (ale przygotuj się!). –

+0

Czytam e-booka. Z mojego rozumowania VolatileRead dodaje MemoryBarrier. Nie gwarantuje to jednak, że drugi wątek będzie w stanie odczytać "świeżą" wartość, którą napisał pierwszy wątek. Tak więc, chociaż to działa, myślę, że teoretycznie nie powinien (przynajmniej używać metod VolatileRead i VolatieWrite). – Petrakeas

1

Zarówno Win kiedy wykonać równolegle.

Z dokumentacji (http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.invoke.aspx): Wykonuje każdą z podanych czynności, , ewentualnie równolegle.

+0

+1 dobry połów !!! – Gabber

+3

To wciąż dziwne. aib są niestabilne i są aktualizowane przed sprawdzeniem wartości w drugim zadaniu. Oznacza to, że w momencie, gdy a sprawdza, czy b == 1, przynajmniej musi wynosić 1 i odwrotnie, więc wydaje się, że logicznie najwyżej można wygrać. – GolezTrol

+0

@GolezTrol, który miałby sens, ale mimo że są one zadeklarowane jako zmienne, nie są używane jako takie: dostaje/ustawia bez użycia Volatile.Read/Volatile.Write –