鉴于泛型类型为每种类型组合创建单独的静态字段实例,如果我想在所有类型中都有静态字段,这是一个有效的模式吗?
public class BaseClass
{
public static int P = 0;
}
public class ChildClass<T> : BaseClass
{
public static int Q = 0;
public void Inc()
{
// ChildClass<int> will have a different "Q" than ChildClass<double>
Interlocked.Increment(ref Q);
// all types of ChildClass will increment the same P
Interlocked.Increment(ref P);
}
}
这种做法有什么不安全之处吗?我的玩具示例有效,但我只是想确保没有可怕的副作用,线程后果等等。)
答案 0 :(得分:4)
您可以使用Interlocked.Increment获取更多线程安全的代码。
public void Inc()
{
Interlocked.Increment(ref Q); // ChildClass<int> will have a different "Q" than ChildClass<double>
Interlocked.Increment(ref P); // all types of ChildClass will increment the same P
}
或普通的lock
public class BaseClass
{
protected static int P = 0;
protected static object pLock = new object();
}
public class ChildClass<T> : BaseClass
{
private static int Q = 0;
private static object qLock = new object();
public void Inc()
{
lock(qLock)
{
qLock++;
}
lock(pLock)
{
qLock++;
}
}
}
请注意,对于每个T
,都会有不同的ChildClass<T>.Q
,但只有一个BaseClass.P
。这意味着您必须使用单独的锁定对象来处理Q
和P
(技术上,您用来锁定P
的任何内容也可用于锁定所有Q
'但这可能不是你想要做的事情。)
答案 1 :(得分:2)
您的模式有效且没有问题。没有静态继承这样的东西,但是您可以像通常那样访问任何类型的静态成员(前提是它们对派生类是可见的,例如不是private
):
BaseClass.P = 10;
ChildClass<string>.Q = 20;
C#规范声明对32位整数(即int
)的单次读取或写入是原子,这意味着它们可以在一条指令内完成,而且永远不会在一半 - 写入的变量对另一个线程可见。
12.5变量引用的原子性
以下数据类型的读写应为原子:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型。此外,在先前列表中具有基础类型的枚举类型的读取和写入也应该是原子的。其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不一定是原子的。
当然,阅读和写作并不保证是原子的。例如,递增变量的值需要先读取然后写入,这就是Interlocked
methods发挥作用的地方。
顺便说一句,您可能知道只有一个P
静态变量,但是Q
个静态变量与您的类型ChildClass<T>
的不同通用实例一样多