New in .NET 4.5: ThreadLocal.Values

Igor Ostrovsky - MSFT

Available since .NET 4, ThreadLocal<T> is a container that holds a separate value for every thread. In practice, ThreadLocal<T> is often convenient for storing per-thread counters, resources, or partial results.

As mentioned earlier on this blog, we have been thinking about adding a Values property to enumerate over the values from all threads that ever stored a value into the ThreadLocal<T> instance. After getting helpful feedback from this blog and other sources, we ended up implementing the capability in .NET 4.5.

To use the Values property, you have to initialize the ThreadLocal<T> instance by setting the constructor argument trackAllValues= true. The sample below demonstrates this usage:

var localResult = new ThreadLocal<int>(() => 0, trackAllValues: true);
Parallel.For(0, 10000, i =>
{
    localResult.Value += Compute(i);
});

int result = localResult.Values.Sum();

Explicit Opt-In

If you don’t set the trackAllValues constructor argument to true, accessing the Values property will throw an InvalidOperationException. The Values property requires additional bookkeeping, and so ThreadLocal<T> requires that you declare upfront if you plan to access Values.

Most importantly, trackAllValues=true changes the lifetime of the values stored into a ThreadLocal<T>. By default (i.e., trackAllValues=false), a value stored into a ThreadLocal<T> is cleared when the ThreadLocal<T> instance is disposed/finalized OR when the corresponding thread exits, whichever happens first. This is the same behavior that ThreadLocal<T> had in .NET 4, and can be especially important when T is a large reference type.

However, removing a value when its corresponding thread exits would make the Values property poorly behaved. For example, in the code sample shown earlier in this blog post, the final summation should clearly process all partial results. It would be unacceptable to omit one of the partial results, say because the ThreadPool has decided to retire the thread that computed it.

So, if you set trackAllValues=true, the ThreadLocal instance will keep track of all values that have been stored into it, even if the corresponding threads are now gone. Then, the values can be returned via the Values property.

More Scenarios

In addition to aggregation of partial results, the Values property can be useful for resource cleanup. The example below executes a Parallel.For loop, where each thread participating in the loop opens its own connection to the database. Once the loop completes, we can use the Values property to release all connections that have been opened:

var threadDbConn = new ThreadLocal<MyDbConnection>(() => MyDbConnection.Open(), true);
try
{
    Parallel.For(0, 10000, i =>
    {
        var inputData = threadDbConn.Value.GetData(i);
        ...
    });
}
finally
{
    foreach(var dbConn in threadDbConn.Values)
    {
        dbConn.Close();
    }
}

Additionally, readers of this blog suggested in comments that the Values property would be useful to simplify existing parallel loops, to aggregate data parallel code that does not use parallel loops, to implement an optimized object pool, and to gather high-performance statistics.

If you find this new property useful in your code, please let us know in the comments!

0 comments

Discussion is closed.

Feedback usabilla icon