Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it permissible to cache/reuse Thread.GetNamedSlot between threads?

The Thread.GetNamedDataSlot method acquires a slot name that can be used with Thread.SetData.

Can the result of the GetNamedDataSlot function be cached (and reused across all threads) or should it be invoked in/for every thread?

The documentation does not explicitly say it "shouldn't" be re-used although it does not say it can be either. Furthermore, the example shows GetNamedDataSlot used at every GetData/SetData site; even within the same thread.

For example (note that BarSlot slot is not created/assigned on all specific threads that the TLS is accessed);

public Foo {
    private static LocalStorageDataSlot BarSlot = Thread.GetNamedDataSlot("foo_bar");

    public static void SetMethodCalledFromManyThreads(string awesome) {
        Thread.SetData(BarSlot, awesome);
    }

    public static void ReadMethodCalledFromManyThreads() {
        Console.WriteLine("Data:" + Thread.GetData(BarSlot));
    }
}

I asks this question in relationship to code structure; any micro performance gains, if any, are a freebie. Any critical issues or performance degradation with the reuse make it not a viable option.

like image 498
user2864740 Avatar asked Jul 11 '17 22:07

user2864740


1 Answers

Can the result of the GetNamedDataSlot function be cached (and reused across all threads) or should it be invoked in/for every thread?

Unfortunately, the documentation isn't 100% clear on this point. Some interesting passages include…

From Thread.GetNamedDataSlot Method (String):

Data slots are unique per thread. No other thread (not even a child thread) can get that data

And from LocalDataStoreSlot Class:

The data slots are unique per thread or context; their values are not shared between the thread or context objects

At best, these make clear that each thread gets its own copy of the data. But the passages can be read to mean either that the LocalDataStoreSlot itself is per-thread, or simply the data to which it refers is per-thread. I believe it's the latter, but I can't point to a specific MSDN page that says so.

So, we can look at the implementation details:

There is a single slot manager per process, which is used to maintain all of the per-thread slots. A LocalDataStoreSlot returned in one thread can be passed to another thread and used there, and it would be owned by the same manager, and use the same slot index (because the slot table is also per-process). It also happens that the Thread.SetData() method will implicitly create the thread-local data store for that slot if it doesn't already exist.

The Thread.GetData() method simply returns null if you haven't already set a value or the thread-local data store hasn't been created. So, the behavior of GetData() remains consistent whether or not you have called SetData() in that thread already.

Since the slots are managed at a process-level basis, you can reuse the LocalDataStoreSlot values across threads. Once allocated, the slot is used up for all threads, and the data stored for that slot will be unique for each thread. Sharing the LocalDataStoreSlot value across threads shares the slot, but even for a single slot, you get thread-local storage for each thread.

Indeed, looking at it this way, the implementation you show would be the desirable way to use this API. After all, it's an alternative to [ThreadStatic], and the only way to ensure a different LocalDataStoreSlot value for each thread in your code would be either to use [ThreadStatic] (which if you wanted to use, you should have just used for the data itself), or to maintain your own dictionary of LocalDataStoreSlot values, indexed presumably by Thread.ManagedThreadId.

Personally, I'd just use [ThreadStatic]. MSDN even recommends this, and it has IMHO clearer semantics. But if you want to use LocalDataStoreSlot, it seems to me that the implementation you have is correct.

like image 165
Peter Duniho Avatar answered Oct 30 '22 11:10

Peter Duniho