2011-05-28 8 views
13

Dokumentacja dla LazyThreadSafetyMode stwierdza, że ​​użycie wartości ExecutionAndPublication może spowodować zakleszczenia, jeśli metoda inicjowania (lub domyślny konstruktor, jeśli nie ma metody inicjowania) używa wewnętrznych blokad. Próbuję lepiej zrozumieć przykłady, które mogą spowodować zakleszczenie podczas korzystania z tej wartości. Przy używaniu tej wartości inicjuję ChannelFactory. Nie widzę konstruktora ChannelFactory za pomocą jakichkolwiek wewnętrznych blokad (przeglądanie klasy z Reflectorem), więc uważam, że ten scenariusz nie pasuje do możliwej sytuacji zakleszczenia, ale jestem ciekawy, jakie sytuacje mogą spowodować zakleszczenie, a także czy może być możliwe impas inicjalizujący ChannelFactory.Lazy <T> ExecutionAndPublication - Przykłady, które mogą spowodować zamknięcie na klucz

Tak więc, aby podsumować moje pytania to:

  1. Czy to możliwe, aby doprowadzić do impasu Inicjowanie ChannelFactory korzystając ExecutionAndPublication?

  2. Jakie są możliwe sposoby spowodowania impasu inicjowania innych obiektów przy użyciu ExecutionAndPublication?

Załóżmy, że mamy następujący kod:

class x 
{ 
    static Lazy<ChannelFactory<ISomeChannel>> lcf = 
     new Lazy<ChannelFactory<ISomeChannel>>(
     () => new ChannelFactory<ISomeChannel>("someEndPointConfig"), 
     LazyThreadSafetyMode.ExecutionAndPublication 
     ); 

    public static ISomeChannel Create() 
    { 
     return lcf.Value.CreateChannel(); 
    } 
} 

Odpowiedz

9
  1. To co zostało udokumentowane - jeśli nie używa żadnych blokad, to użycie może nie powodować żadnych zakleszczenia.
  2. Wyobraź sobie, że masz leniwą wartość, którą zainicjujesz przez odczytanie z bazy danych, ale chcesz się upewnić, że tylko jeden wątek uzyskuje dostęp do bazy danych w dowolnym momencie. Jeśli masz inny kod, który uzyskuje dostęp do DB, możesz mieć zakleszczenie. Rozważmy następujący kod:
void Main() 
{ 
    Task otherThread = Task.Factory.StartNew(() => UpdateDb(43)); 
    Thread.Sleep(100); 
    Console.WriteLine(lazyInt.Value); 
} 

static object l = new object(); 
Lazy<int> lazyInt = new Lazy<int>(Init, LazyThreadSafetyMode.ExecutionAndPublication); 

static int Init() 
{ 
    lock(l) 
    { 
     return ReadFromDb(); 
    } 
} 

void UpdateDb(int newValue) 
{ 
    lock(l) 
    { 
     // to make sure deadlock occurs every time 
     Thread.Sleep(1000); 

     if (newValue != lazyInt.Value) 
     { 
      // some code that requires the lock 
     } 
    } 
} 

Init() odczytuje z DB, więc musi korzystać z blokady. UpdateDb() zapisuje do bazy danych, więc również potrzebuje blokady, a ponieważ Lazy używa w tym przypadku również blokady wewnętrznej, powoduje to zakleszczenie.

W takim przypadku łatwo byłoby naprawić zakleszczenie, przenosząc dostęp do lazyInt.Value w UpdateDb() poza instrukcją blokady, ale w innych przypadkach może to nie być tak banalne (lub oczywiste).

+1

Świetna odpowiedź @svick - klasyczny przykład zagnieżdżonych zamków nabytych w odwrotnej kolejności, jest to zgodne z tym, o czym myślałem - świetnym przykładem do wyjaśnienia scenariusza, dziękuję! – dugas

Powiązane problemy