2013-06-15 9 views
7

Oto moje własne narzędzie do podwójnego sprawdzania blokowania: jest to statyczna metoda, do której podajesz kryterium, obiekt synchronizacji i akcję, która ma zostać wykonana.Czy jest możliwe utworzenie testu pokazującego niepowodzenie podwójnie sprawdzanego blokowania w języku C#?

public static bool RunIf(Func<bool> criterion, object syncObject, Action action) 
{ 
    if (criterion()) 
     lock(syncObject) 
      if (criterion()) 
      { 
       Thread.MemoryBarrier(); 
       action(); 
       return true; 
      } 
    return false; 
} 

ja dać do zrozumienia, że ​​w zależności od specyfikacji C# jest możliwe do optymalizacji zmienić kolejność alokacji pamięci w taki sposób, że bez bariery pamięci, technika ta może dać fałszywie dodatnich i wykonać działanie, kiedy nie powinno.

W moim małym świecie, jeśli taka awaria jest możliwa, powinno być również możliwe opracowanie testu, który wykaże awarię konsekwentnie przez trafienie scenariusza wystarczająco mocno z wystarczającą liczbą równoległych przypadków testowych. Tego rodzaju test szukam od około roku, ale do tej pory narysowałem pustkę. Czy ktoś może mi pokazać test, który:

  1. pokazuje awarię tej metody w przypadku braku bariery pamięci;

  2. pokazuje swój sukces po powtórzeniu testu z przywróconą barierą pamięci?

+3

Wierzę, że framework .Net na architekturze x86 ma silniejszy model pamięci niż wymaga specyfikacji C#, więc nie będzie można utworzyć takiego testu. Możesz mieć więcej szczęścia na ARM (np. Windows RT), który ma słabszy model pamięci. – svick

+3

svick jest poprawny. Sprawdź [ten artykuł MSDN] (http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S5), który mówi o silniejszym modelu pamięci dla .Net 2+. –

Odpowiedz

1

Nie, to nie jest możliwe, aby przetestować niedeterministycznego zachowanie (lub przynajmniej, jeśli nie następnie ujemny wynik jest wciąż niejednoznaczne).

+0

Zgadzam się, można wymyślić test, aby uzyskać dwa wątki, aby wywołać metodę. Nie można jednak deterministycznie przełączać między dwoma wątkami. Wdrożony wzorzec jest jednak dobrze znany i powinien zostać przeprowadzony prosty przegląd kodu. –

+0

To rodzi kolejne pytanie. Można całkowicie zrezygnować z podwójnie sprawdzonego blokowania i wdrożyć prosty, niebezpieczny singletowy wzór. Chociaż nie ma gwarancji, że to się nie powiedzie, gdy zostanie trafiony przez równoległe wątki - to jest, jak mówisz, zachowanie niedeterministyczne - jeśli trafisz na niego kilka milionów równoległych wątków, szanse na porażkę są tak wielkie, że jest to faktycznie test deterministyczny. Czy to byłaby zła praktyka? –

0

Niemożliwe jest skonstruowanie takiego testu, ponieważ instrukcja "blokada" tworzy już pełne ogrodzenie (to jest ogrodzenie przejmujące i zwalniające) dla zablokowanego bloku instrukcji. Zatem dodatkowy Thread.MemoryBarrier() jest zbędny i nie daje żadnego efektu.

Tak więc, w zależności od tego, co zrobił delegat criterion, twoja konstrukcja RunIf jest bezpieczna lub całkowicie niepotrzebna.

Co mam na myśli to, że jeśli przekazany criterion delegata czerpie swoją wartość zwracaną od stanu zmiennego, a ten stan zmienny nie jest zabezpieczony (bezpośrednio lub pośrednio) przez tego samego syncObject, ten kod nie jest bezpieczny i RunIf zasadniczo nie ma żadnego efektu, z wyjątkiem otaczania instrukcji wykonanych podczas drugiego połączenia z delegatem criterion z pełnym ogrodzeniem.

Powiązane problemy