Jak nie używać blokad lock

Słowo lock służy do synchronizacji  kodu między wątkami. Często jednak przeglądając kod widzę niepoprawne użycie. Rozważmy następujący przykład:

public class OrderManager
{
    public void Submit()
    {
        lock(this)
        {
            //...
        }
    }
}

Przedstawiona konstrukcja może spowodować wiele trudnych do wykrycia problemów. Co jeśli użytkownik naszej biblioteki również wykorzysta źle lock i napisze:

OrderManager orderManager=new OrderManager();
//...
lock(orderManager)
{
    orderManager.Submit();
}

Spowoduje to oczywiście deadlock’a. Pierwszy lock się uda (na orderManager), z kolei na drugim wykonanie się zakleszczy. Nie możemy wymagać od użytkowników naszego kodu, że wiedzą jakiego typu lock’i zaimplementowaliśmy. Musimy zawsze pisać kod, który będzie działać w jak największej liczbie przypadków użycia. Rozwiązanie jest proste:

public class OrderManager
{
    private object _sync=new object();
    
    public void Submit()
    {
        lock(_sync)
        {
            //...
        }
    }
}

Jeśli wykorzystujemy rzadko Lock ,warto dodać lazy loading dla _sync:

public class OrderManager
{
    private object _sync;
    
    public void Submit()
    {
        lock(GetSyncHandle())
        {
            //...
        }
    }
    private object GetSyncHandle()
    {
        System.Threading.Interlocked.CompareExchange(ref _sync, new object(), null);
          return _sync;
    }
}

CompareExchange gwarantuje atomowość operacji – dzięki temu wiemy, że dokładnie jedna kopia _sync zostanie utworzona.

4 thoughts on “Jak nie używać blokad lock”

  1. Czy lock przypadkiem nie sprawdza czy poprzednie lockowanie pochodzi z bierzacego watku. Jesli tak to podnosi tylko licznik blokad (z tego co wiem modyfikator synchronized javy dziala tworzac blokade na obiekcie w kontekscie ktorego zostala ta metoda wywolana). Ogolnie temat do weryfikacji.

  2. 1) Szogun ma rację. Ale nie zmienia to faktu, że użytkownik biblioteki tak czy siak może zalockować obiekt z innego wątku, a z innego wywołać Submit() 🙂

    2) lazy loading dla “new object()” to moim zdaniem lekka przesada; kiedy takie coś nie spełni swojego zadania:
    private static object _lock = new object();
    ?

  3. Hey
    1. true
    2. lazy loading: zalezy od przypadku konkretnego. Dla submit to pewnie przesada.
    Pozdrawiam
    Piotr

  4. Jeszcze jedno:)
    @szogun: przedstawiony kod nie tworzy wątków ale oczywiście jeśli przykład pokazuje lock, to mam na myśli, środowisko wielowątkowe. lock(this) to popularny problem, pewnie na MSDN też by się znalazło takie same wyjaśnienie jak w tym poście – to nie jest mój jakiś wymysł:)
    Pozdrawiam
    Piotr

Leave a Reply

Your email address will not be published.