【发布时间】:2019-10-20 00:44:51
【问题描述】:
我在静态类中有一个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?
【问题讨论】:
-
ThreadStatic 和线程池是一个非常糟糕的组合。 AsyncLocal 是专门为解决这个问题而发明的。
-
@HansPassant 是的,我意识到这一点,但 AsyncLocal 也没有解决我的问题..
-
很难说,我觉得还可以。您不能假设静态构造函数在任何特定线程上运行。
-
@Tanzeel - 我相信你在滥用
AsyncLocal,在async流之外使用它。混合static AsyncLocal和Parallel Task Library是未定义的,AFAIK。如果您希望在重新使用线程时将静态值设置为新值,则需要编写代码来执行此操作。一个干净的方法是定义一个class MyContext,它包含您需要的所有信息。然后你可以只有一个静态:[ThreadStatic] static MyContext myContext;你仍然需要在停止使用线程时手动将其设置为 null - 但至少这样做变得更易于管理。
标签: c# multithreading thread-local threadstatic callcontext