Klasa ThreadLocal

W ostatnim poście opisałem atrybut ThreadStatic pozwalający na korzystanie ze statycznych pól w sposób niezależny w każdym wątku. Dziś pora na analogiczną klasę, wprowadzoną w .NET 4.0 – ThreadLocal. Zacznijmy od przykładu:

class ClassA
{    
    private ThreadLocal<int> _value=new ThreadLocal<int>();

    public void RunAsync(object value)
    {
        var thread = new Thread(Run);
        thread.Start(value);
        thread.Join();
    }
    private void Run(object value)
    {
        if(value!=null)
            _value.Value = (int)value;

        Console.WriteLine(_value);
    }
}

Następnie wywołujemy RunAsync:

var classA = new ClassA();
classA.RunAsync(2);
classA.RunAsync(null);      

Bez użycia ThreadLocal, otrzymalibyśmy na ekranie 2,2. Powyższy przykład używa jednak ThreadLocal więc pola są niezależne – na ekranie wyświetli się 2,0.

Należy jednak uważać na wątki z puli. Jak wiemy, wątek z puli po zakończeniu działania jest zwracany z powrotem do puli a nie niszczony. Z tego względu może się okazać, że dwa wątki odpalone jeden po drugim będą współdzielić taką samą wartość- jeden się zakończy i potem ten sam będzie uruchomiony.

ThreadLocal umożliwia jeszcze Lazy Loading. Jeśli wartość nie zostanie jawnie ustawiona wtedy można skorzystać z lazy loading aby ją określić np.:

class ClassA
{
    private ThreadLocal<int> _value = new ThreadLocal<int>(() => 5);

    public void RunAsync(object value)
    {
        var thread = new Thread(Run);
        thread.Start(value);
        thread.Join();
    }
    private void Run(object value)
    {
        if(value!=null)
            _value.Value = (int)value;

        Console.WriteLine(_value);
    }
}

W przypadku gdy _value.Value nie zostało ustawione, wtedy próba wywołania _value.Value zwróci zawsze 5. Stanowi to zaletę w porównaniu do ThreadStatic gdzie nie mogliśmy określić wartości domyślnej. Druga oczywista zaleta to fakt, że ThreadStatic dotyczył wyłącznie statycznych pól.

2 thoughts on “Klasa ThreadLocal”

  1. Odnośnie problemu dot. tego, że ThreadLocal jest per wątek, a nie Task — w książce pt. “Pro .NET 4 Parallel
    Programming in C#” piszą, że “To ensure that you get the results you expect with Tasks, make sure that you set the ThreadLocal.Value property at the start of your Task body”.

    Jak w praktyce wygląda stosowanie tej “reguły”? Czy można polegać na tym, że użycie ThreadLocal w pierwszej instrukcji kodu wykonywanego jako Task faktycznie *zawsze* powoduje uruchomienie go w oddzielnym wątku?

  2. Co do puli to tak naprawde nie stanowi to problemu. Nigdy sie nie zdazy ze dwa uruchumione watki beda dzielily taka sama wartosc. Jedynie co moze sie przytrafic to sytuacja w ktorej Watek A konczy zadanie, umieszczany jest z powrotem w puli a nastpenie nowy watek jest zdejmowany z tym samym id – bedize mial wiec taka sama wartosc shared. Jak jednak powiedziales, wystarczy zadbac aby kazdy watek ustawial wartosc poczkowa lokalnie, w watku.

Leave a Reply

Your email address will not be published.