[ThreadStatic]
用于.NET框架中的各个位置,为各种功能提供环境上下文(例如Transaction.Current
,用于TransactionScope
)。
不幸的是,这意味着执行一些线程杂耍的功能(ASP.NET、异步关键字代码)会切换线程,但不会复制TransactionScope
,因此像TransactionScope
这样的功能无法正常工作。
还有另一种机制CallContext.LogicalGetData
(此处详细介绍),它可以在线程切换期间正确地跨状态复制(至少在.NET 4.5中是这样)。在我看来,TransactionScope
如果使用它而不是[ThreadStatic]
会更好。
如果使用[ThreadStatic]
的功能是今天编写的,而不是具有向后兼容性要求的现有功能,它们会使用CallContext.(G|S)etLogicalData
编写吗?
实际上,它们有非常不同的用例。
- CCD_ 11不能通过CCD_ 12或类似的上下文开关传递值
CallContext
无法为每个线程保留一个值
所以你看,一个不能取代另一个。CCD_ 14是一个低级原语。我认为在CallContext
等出现之后,它的用例并没有减少。注意,它的用例非常小——我想我上一次使用它可能是两年多前。
我会把Transaction.Current
之类的东西描述为TLS的滥用。它从来没有为此而设计,所以当TLS似乎破坏了异步时,这只是因为它一开始就不应该用于此。
是。我确信这在当时看起来是个好主意,但[ThreadStatic]让开发人员产生了一种虚假的安全感。ThreadStatic字段具有全局变量的许多缺点,唯一的优点(不同的线程有自己的全局实例)也有相应的缺点(如果切换线程,全局实例就会消失)。这不是一场胜利。全局变量很糟糕,线程静态全局变量和常规全局变量一样糟糕。
我在一个相当大的代码库中工作,几年前有几十名开发人员(如果可以的话,可能有几百名在过去几年中离开团队的人),[ThreadStatic]经常攻击我们。我们有大量的代码,它们在背后使用ThreadStatic全局,如果你在工作线程上做任何事情,它当然会中断。就像我说的,我相信这在当时看起来是个好主意。。。但它现在花了我们比以往任何时候都多的钱。
另一种选择是将相同的对象(HttpContext、Transaction等)传递给依赖它的每个方法。这需要更多的类型,但在我看来,从长远来看还是更好的。