我有一个非常古老但非常大的库,我正在考虑转换为C#类库。现有库使用存储在TLS中的许多全局变量。 C#没有真正的全局变量概念,但一种解决方法是使用一个名为GlobalVar的静态类,并将它们全部放在这个类中,以便可以通过GlobalVar.xxxxxx访问它们
但是,我的想法是,这会破坏所有正在转换的现有代码,因为GlobalVar类将是一个普通的全局类而不是每个线程存储。有没有办法让这些全局变量成为每个线程?即C#中的__declspec(thread)静态等价是什么?
我应该补充一点,我讨厌全局变量。我认为它们往往是设计不佳的结果。但是,由于严格的时间限制,第一阶段是将库转换为C#,而不是大惊小怪,然后第二阶段将重新设计它们。
答案 0 :(得分:14)
有ThreadLocal类(在4.0中引入)和ThreadStaticAttribute。
ThreadStaticAttribute
只能在static
字段中使用。 ThreadLocal
类可用于“普通”字段,但速度较慢。
请注意,如果你不控制你所在的线程(例如你是一个ASP.NET页面,你开始使用“随机”预先使用的线程,或者你是一个ThreadPool的线程) ,那么你的“线程静态”(通常不是属性)变量将使用前一个线程的旧值进行预初始化。 (参见例如A tale of two techniques: The [ThreadStatic] Attribute and System.Web.HttpContext.Current.Items)
我忘记了,Thread.AllocateDataSlot的“目标”与其他目标相似。
答案 1 :(得分:4)
假设你要使用.NET 4.0,你可以static ThreadLocal<ThreadLocalData>
将ThreadLocalData
类的所有变量都作为属性:
class ThreadLocalData
{
public int GlobalInt { get; set; }
public string GlobalString { get; set; }
}
class Global
{
static ThreadLocal<ThreadLocalData> _ThreadLocal =
new ThreadLocal<ThreadLocalData>( () => new ThreadLocalData() );
public static ThreadLocalData ThreadLocal
{
get { return _ThreadLocal.Value; }
}
}
然后您将访问以下属性:
int i = Global.ThreadLocal.GlobalInt;
您可以添加任何非线程局部的全局变量作为Global
类的常规属性。
答案 2 :(得分:3)
您可以使用[ThreadStatic]
属性实现相同的线程本地存储,或使用ThreadLocal
类在.Net 4中实现。
[ThreadStatic]
private static string MyThreadGlobal;
private ThreadLocal<string> MyThreadGlobal = new ThreadLocal<string>();
还有CallContext类,但其他方法可能更受欢迎。
答案 3 :(得分:0)
有三种主要方法允许线程专有地访问其自身版本的线程不安全 对象。
1- [ThreadStatic]
实现非常简单,可以通过使用[ThreadStatic]
属性签名静态字段来完成
[ThreadStatic] static int y;
现在,每个线程都将看到y的单独副本;
不幸的是,[ThreadStatic]
不适用于实例字段。
2- ThreadLocal
它是framework 4.0
的新增功能,它为静态字段和实例字段提供线程本地存储。
另外,您可以为每个线程提供默认值,并且该值会被延迟计算。
static ThreadLocal<int> y = new ThreadLocal<int> (() => 10); //Static variable
ThreadLocal<int> y = new ThreadLocal<int> (() => 10); //Instance variable
3- GetData和SetData
在这种方法中,使用Thread
类的两个方法:GetData
和SetData
。
这些方法将数据存储在特定于线程的“插槽”中。应该为
插槽,因此可以在所有线程上使用相同的插槽,并且它们将获得单独的值。
// The same LocalDataStoreSlot object can be used across all threads.
LocalDataStoreSlot y= Thread.GetNamedDataSlot ("slotName");
object data = Thread.GetData (y);
Thread.SetData (y, value)