Search code examples
.netasynchronoustransactionscopethreadstatic

Is the use of ThreadStatic in .NET framework code a harmful relic of a bygone age?


[ThreadStatic] is used in various places in the .NET framework to provide an ambient context for various features (e.g. Transaction.Current, which is used for TransactionScope).

Unfortunately, this means that features which do some thread juggling (ASP.NET, async keyword code) switch threads, but don't copy the TransactionScope, so features like TransactionScope don't work as you might expect.

There is another mechanism, CallContext.LogicalGetData (more here) which does copy across state during thread switches correctly (at least in .NET 4.5). It seems to me that TransactionScope would be better if it used this rather than [ThreadStatic].

If the features that are using [ThreadStatic] were written today, rather than being existing features with requirements of backwards compatability, would they be written using CallContext.(G|S)etLogicalData?


Solution

  • In reality they have very different use cases.

    • ThreadStatic can't transfer a value across an await or similar context switch.
    • CallContext can't retain a value per-thread.

    So you see, one can not replace the other. ThreadStatic is a low-level primitive. I don't think its use cases have gotten any fewer after CallContext etc. came along. Note, its use cases are vanishingly small -- I think the last time I used it was probably over two years ago.

    I would characterize things like Transaction.Current as an abuse of TLS. It was never designed for this, and so when TLS appeared to break async, it was only because it never should have been used for that in the first place.