我在静态课程中有一个ThreadStatic
成员。静态类在多线程环境中使用。我想确保当线程返回到线程池(或重新使用)时,该成员将被处置(或重新初始化),因此特定线程的任何后续使用都将获得该变量的新副本。该成员必须保持静态,因此实例成员将无济于事。
我尝试使用ThreadLocal
,AsyncLocal
和CallContext
,但这些方法都没有帮助。 (CallContext
主要用于概念验证,它是.net标准应用程序,因此callcontext仍然无法正常工作。)
这只是我编写的示例代码,用于重新创建具有ThreadStatic
,ThreadLocal
,AsyncLocal
和CallContext
进行测试的问题。
class Program
{
static void Main(string[] args)
{
var act = new List<Action<int>>()
{
v=> ThreadClass.Write(v),
v=> ThreadClass.Write(v),
};
Parallel.ForEach(act, new ParallelOptions { MaxDegreeOfParallelism = 1 }, (val, _, index) => val((int)index));
Console.WriteLine($"Main: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadClass.ThreadStatic} ThreadLocal = {ThreadClass.ThreadLocal.Value} AsyncLocal = {ThreadClass.AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");
Console.ReadKey();
}
}
public static class ThreadClass
{
static object _lock = new object();
[ThreadStatic]
public static string ThreadStatic;
public static ThreadLocal<string> ThreadLocal = new ThreadLocal<string>(() => "default");
public static readonly AsyncLocal<string> AsyncLocal = new AsyncLocal<string>();
public static string CallContextData
{
get => CallContext.LogicalGetData("value") as string;
set => CallContext.LogicalSetData("value", value);
}
static ThreadClass()
{
AsyncLocal.Value = "default";
}
public static void Write(int id)
{
lock (_lock)
{
Console.WriteLine($"{id} Init: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadStatic} ThreadLocal = {ThreadLocal.Value} AsyncLocal = {AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");
ThreadStatic = $"Static({id})";
ThreadLocal.Value = $"Local({id})";
AsyncLocal.Value = $"Async({id})";
CallContextData = $"Call({id})";
Console.WriteLine($"{id} Chng: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadStatic} ThreadLocal = {ThreadLocal.Value} AsyncLocal = {AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");
}
}
}
上面的代码在单个线程中运行,因此可以重复使用该线程。
0 Init: ThreadId: 1 ThreadStatic = ThreadLocal = default AsyncLocal = default CallContext:
0 Chng: ThreadId: 1 ThreadStatic = Static(0) ThreadLocal = Local(0) AsyncLocal = Async(0) CallContext: Call(0)
--------------------
1 Init: ThreadId: 1 ThreadStatic = Static(0) ThreadLocal = Local(0) AsyncLocal = Async(0) CallContext: Call(0)
1 Chng: ThreadId: 1 ThreadStatic = Static(1) ThreadLocal = Local(1) AsyncLocal = Async(1) CallContext: Call(1)
--------------------
Main: ThreadId: 1 ThreadStatic = Static(1) ThreadLocal = Local(1) AsyncLocal = CallContext:
但是,从输出中可以看到,当进行第二次调用并重用线程1时,它仍然具有由线程0设置的值。
在重新使用线程时,有什么方法可以将ThreadStatic
变量重置为默认值或为null?
答案 0 :(得分:1)
TL; DR
如果不希望变量在多线程应用程序中被多个线程重用,则没有理由将其设为静态。
如果我们不希望变量被同一线程重用,那么为什么我们故意使用[ThreadStatic]
是有疑问的,因为那是我们允许这样做的原因。
我正在关注ThreadStatic
方面,因为它似乎是问题的焦点。
因此特定线程的任何后续使用都将获得该变量的新副本。
使用线程不需要它们自己的变量副本-使用该变量的方法可以或可以不需要它们自己的变量副本。这听起来像是一团乱七八糟的话,但是线程本身不需要任何变量的副本。它可能所做的事情与此静态类和此变量无关。
当我们使用该变量时,我们会在乎它是否是“新副本”。也就是说,当我们调用使用该变量的方法时。
如果,当我们使用静态变量(在方法外部声明)时,我们想要的是确保在使用它之前先对其进行新实例化,并在完成该操作后将其释放,那么我们就可以实现在使用它的方法中。我们可以实例化,处理它,甚至可以根据需要将其设置为null
。但是,这样做的结果是,通常可以消除在使用该方法的方法之外声明该变量的任何需要。
如果我们这样做:
public static class HasDisposableThreadStaticThing
{
[ThreadStatic]
public static DisposableThing Foo;
public static void UseDisposableThing()
{
try
{
using (Foo = new DisposableThing())
{
Foo.DoSomething();
}
}
finally
{
Foo = null;
}
}
}
我们已经完成了目标。
在重用线程时,是否可以将ThreadStatic变量重置为默认值或为null?
完成。每次同一线程进入方法时(“重新使用该线程”),它为null。
但是如果这就是我们想要的,那为什么不这样做呢?
public static class HasDisposableThreadStaticThing
{
public static void UseDisposableThing()
{
using (var foo = new DisposableThing())
{
foo.DoSomething();
}
}
}
结果完全相同。每个线程都以DisposableThing
的新实例开头,因为它在执行方法时会声明变量并创建一个新实例。而不是将引用设置为null
超出了范围。
两者之间的唯一区别是,在第一个示例中,DisposableThing
在类之外公开公开。这意味着其他线程可以使用它而不是声明自己的变量,这很奇怪。由于他们还需要确保在使用它之前将其实例化,因此为什么不像第二个示例那样也只创建自己的实例?
确保变量在静态方法中每次需要初始化和处置时,最简单,最普通的方法是在静态方法中局部声明该变量并创建一个新实例。然后,无论有多少线程同时调用它,它们都将使用单独的实例。