很多编码规范上都指出不要使用线程静态(ThreadStatic),这主要是为了防止在使用的时候出现意外。当有的时候这个线程静态确实会带来一些好处,如何取舍就要看具体情况了。
首先,明确一点线程静态与标准静态之间的区别。线程静态相当于变量有线程来负责保存,因此每个线程看到都可以是不同的,但在同一个线程内是相同的。而标准静态实际上是应用程序域(AppDomain)负责保存的,因此在相同应用程序域中都看到相同的对象,而在多应用程序域的程序中,每个应用程序域中的值可以是不同的。
两者在作用范围上的不同,导致了很多的特性上的不同。而这些通常会被混淆,这也就是为什么很多编程规范上指出不要使用线程静态的一个根本原因之一吧。
首先,两种静态的GC回收条件不同。线程静态所在的那个线程一旦停止,GC就可以回收线程静态所引用的对象了。而标准静态只有在整个应用程序域被卸载的时候,GC才可以回收这些被引用的对象(如果是单个应用程序域的程序,就意味着必须等到整个应用程序退出)。当然,如果线程静态是在应用程序的主线程上的话,GC就只能等到整个应用程序退出的时候了。
第二,类型初始化时对静态值的初始化效果不同。标准的静态很好理解,只要做了初始化,那就一定是这个初始值。但线程静态的类型初始化有点特别,类型初始化仅仅发生在第一次用到这个类型的线程上,因此,只有这个线程的线程静态获得了初始化,而其他线程上的这个线程静态是未设值的状态。
第三,线程静态的对象天生是线程安全的。例如,要做一个相对于线程来说SingleTon的对象,可以非常简单的写成:
Lock完全没有存在的必要,因为不可能有两个线程访问到相同的_instance。
第四,线程静态容易犯以下错误:
1、在不同的线程中期望获得相同的值。
这个很好理解,使用者没有搞清楚这是两个完全不同的线程,这是个非常容易犯的错误。
2、相同的线程中期望获得不相同的值。
这个可能有点意外,使用者以为这是两个不同的线程,结果却相反。听起来很荒谬,但确实有可能。如果这个两个使用者以为的线程是通过线程池跑的,那就不难理解了吧。对线程池而言,它是用一个线程模拟了多个线程,因此在这些被模拟的线程上,线程静态的值其实是同一个。
可以看出,线程静态还是有一定风险的用法,毕竟和基于应用程序域的静态有着很大的不同。而且,在一定程度上影响了代码的可读性。所以除非有特别的需要,尽量应该避免线程静态。
首先,明确一点线程静态与标准静态之间的区别。线程静态相当于变量有线程来负责保存,因此每个线程看到都可以是不同的,但在同一个线程内是相同的。而标准静态实际上是应用程序域(AppDomain)负责保存的,因此在相同应用程序域中都看到相同的对象,而在多应用程序域的程序中,每个应用程序域中的值可以是不同的。
两者在作用范围上的不同,导致了很多的特性上的不同。而这些通常会被混淆,这也就是为什么很多编程规范上指出不要使用线程静态的一个根本原因之一吧。
首先,两种静态的GC回收条件不同。线程静态所在的那个线程一旦停止,GC就可以回收线程静态所引用的对象了。而标准静态只有在整个应用程序域被卸载的时候,GC才可以回收这些被引用的对象(如果是单个应用程序域的程序,就意味着必须等到整个应用程序退出)。当然,如果线程静态是在应用程序的主线程上的话,GC就只能等到整个应用程序退出的时候了。
第二,类型初始化时对静态值的初始化效果不同。标准的静态很好理解,只要做了初始化,那就一定是这个初始值。但线程静态的类型初始化有点特别,类型初始化仅仅发生在第一次用到这个类型的线程上,因此,只有这个线程的线程静态获得了初始化,而其他线程上的这个线程静态是未设值的状态。
第三,线程静态的对象天生是线程安全的。例如,要做一个相对于线程来说SingleTon的对象,可以非常简单的写成:
public class Foo
{
[ThreadStatic]
private static Foo _instance;
private Foo() { }
public static Foo Instance
{
get
{
if (_instance == null)
_instance = new Foo();
return _instance ;
}
}
}
{
[ThreadStatic]
private static Foo _instance;
private Foo() { }
public static Foo Instance
{
get
{
if (_instance == null)
_instance = new Foo();
return _instance ;
}
}
}
Lock完全没有存在的必要,因为不可能有两个线程访问到相同的_instance。
第四,线程静态容易犯以下错误:
1、在不同的线程中期望获得相同的值。
这个很好理解,使用者没有搞清楚这是两个完全不同的线程,这是个非常容易犯的错误。
2、相同的线程中期望获得不相同的值。
这个可能有点意外,使用者以为这是两个不同的线程,结果却相反。听起来很荒谬,但确实有可能。如果这个两个使用者以为的线程是通过线程池跑的,那就不难理解了吧。对线程池而言,它是用一个线程模拟了多个线程,因此在这些被模拟的线程上,线程静态的值其实是同一个。
可以看出,线程静态还是有一定风险的用法,毕竟和基于应用程序域的静态有着很大的不同。而且,在一定程度上影响了代码的可读性。所以除非有特别的需要,尽量应该避免线程静态。