在 dotnet 程序提供了一个好用的特性,可以让字段作为线程的静态字段,也就是在相同线程的所有代码访问的静态字段是相同对象,但不同线程访问的时候是不同的
在 .NET 程序可以使用 ThreadStaticAttribute 特性标记在一个静态字段上,这样这个字段就可以做到在线程里面静态
在一个类的静态字段上面添加 ThreadStaticAttribute 可以让这个字段作为线程的静态字段,也就是在相同的线程访问的时候这个字段是静态的,拿到的对象的实例相同,但是在不同的线程拿到不相同
在 ThreadStaticAttribute 支持的只有静态字段,不支持静态属性,不支持普通的字段。同时添加了这个特性的静态字段不支持初始化
下面写一段代码尝试一下
class Foo
{
public static string StaticProperty
{
get => _staticProperty;
set => _staticProperty = value;
}
public static string ThreadStaticProperty
{
get => _threadStaticProperty;
set => _threadStaticProperty = value;
}
[ThreadStatic] private static string _threadStaticProperty = "初始值";
private static string _staticProperty = "初始值";
}
我在一个类创建了两个不同的静态属性,一个是普通的静态属性,另一个是线程静态属性,我尝试都给两个字段初始值
static void Main(string[] args)
{
Foo.StaticProperty = "普通静态属性";
Foo.ThreadStaticProperty = "线程静态属性";
var taskList = new List<Task>();
for (int i = 0; i < 10; i++)
{
var n = i;
var task = new Task(() =>
{
Console.WriteLine(
$"thread={Thread.CurrentThread.ManagedThreadId} 静态属性={Foo.StaticProperty} 线程静态属性={Foo.ThreadStaticProperty} 次数={n}");
Foo.StaticProperty = n.ToString();
Foo.ThreadStaticProperty = n.ToString();
});
task.Start();
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
}
我添加了上面的代码用于多个线程输出值同时修改值,在运行的时候可以看到,对于线程静态属性的输出都是空,即使我在代码添加了初始值。因为线程静态属性不支持给初始值,另外在不同的线程修改的线程静态属性是不会影响其他线程
上面代码的输出如下,可能小伙伴运行的输出和我不相同
thread=9 静态属性=普通静态属性 线程静态属性= 次数=4
thread=10 静态属性=普通静态属性 线程静态属性= 次数=3
thread=4 静态属性=普通静态属性 线程静态属性= 次数=1
thread=11 静态属性=普通静态属性 线程静态属性= 次数=7
thread=5 静态属性=普通静态属性 线程静态属性= 次数=0
thread=6 静态属性=普通静态属性 线程静态属性= 次数=2
thread=7 静态属性=普通静态属性 线程静态属性= 次数=5
thread=8 静态属性=普通静态属性 线程静态属性= 次数=6
thread=10 静态属性=3 线程静态属性=3 次数=9
thread=9 静态属性=3 线程静态属性=4 次数=8
从上面代码可以知道如果想要多个线程之间的静态字段或属性不相互影响,可以通过 ThreadStaticAttribute 如输出的最后两行,可以看到普通静态属性是在所有线程使用相同实例,于是输出的静态属性的值相同。但是线程静态属性是每个线程不相同的,在线程 10 的次数是 3 修改的属性值也就是 3 最后输出的就是 3 同时在线程 9 里面的线程静态属性和上次线程修改的相同
本文用到的类放在github 欢迎小伙伴访问
ThreadStatic静态字段在每个线程里的唯一性 - 王树伦的博客 - CSDN博客
C# [ThreadStatic] 标记静态字段对多线程执行的影响 - Ryan_zheng - 博客园
ThreadStatic特性标记静态字段对多线程执行的影响 - b0b0 - 博客园
本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/dotnet-%E7%BA%BF%E7%A8%8B%E9%9D%99%E6%80%81%E5%AD%97%E6%AE%B5.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 lindexi (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。