2010-11-16 14 views
5

natknąłem się na ten przykład sytuacji wyścigu:rubin warunek prosty wyścig pytanie

def inc(n) 
    n + 1 
end 

sum = 0 

threads = (1..10).map do 
    Thread.new do 
    10_000.times do 
     sum = inc(sum) 
    end 
    end 
end 

threads.each(&:join) 
p sum 

nici biegną w pararell i istnieje szansa, że ​​chociaż jeden wątek odczytuje wartość sumy za, drugi kończy zwiększając ją, ale były kończy właśnie własną inkrementację ze starą wartością, w wyniku czego suma się nie zmieni.

Ale zastanawiałem się, dlaczego, gdy zamieniam wiersz "sum = inc (suma)" na "suma + = 1", wynik wydaje się zawsze poprawny.

Dlaczego tak jest?

Czy to dlatego, że narzut wywoływania metody jest tak duży w porównaniu do zwykłego przypisania zmiennych, a zatem niektóre wątki "tracą synchronizację", powodując, że dane wyjściowe są niepoprawne?

Zakładam, że nawet z prostą sumą + = 1 nadal będę w stanie obserwować stan wyścigu, ale tylko wtedy, gdy robiłem znacznie dłuższą pętlę sumującą, itp.?

Odpowiedz

3

Czy dlatego, że narzut wywołanie metody jest tak ogromna w porównaniu do zaledwie robi przypisanie zmiennej, a więc niektóre wątki „niezsynchronizowane” powoduje wyjście jest nieprawidłowy?

Tak. Aby to zweryfikować, zwiększ licznik i przeprowadź kilka testów. I wzrosła do 100_000.times i oto wyniki:

$ seq 5 | xargs -L 1 ruby a.rb 100000 
451167 
472581 
464413 
442191 
454204 

Cóż, nie wydaje się, że ładne, prawda?

Tak, tak, przyrost nie jest atomowy w Ruby (i wątpię, że jest tam wiele języków). Istnieją jednak klasy pomocnicze do implementacji takiego zachowania; na przykład this one.