实例构造函数设置静态成员,线程安全吗?
-
09-06-2019 - |
题
我正在重构一些代码,并且想知道如何使用 lock
在实例构造函数中。
public class MyClass {
private static Int32 counter = 0;
private Int32 myCount;
public MyClass() {
lock(this) {
counter++;
myCount = counter;
}
}
}
请确认
- 实例构造函数是线程安全的。
- lock 语句防止访问该代码块,而不是静态“计数器”成员。
如果原始程序员的意图是让每个实例知道其“计数”,那么我将如何同步对“计数器”成员的访问以确保另一个线程不会新建 MyClass
并在设置计数之前更改计数?
仅供参考 - 这个类不是单例类。实例必须简单地知道它们的数量。
解决方案
@ajmasstrean
我并不是说你应该使用单例模式本身,而是采用它封装实例化过程的方法。
IE。
- 将构造函数设为私有。
- 创建返回类型的静态实例方法。
- 在静态实例方法中,在实例化之前使用lock关键字。
- 实例化该类型的一个新实例。
- 增加计数。
- 解锁并返回新实例。
编辑
我遇到的一个问题是,你怎么知道计数何时减少?;)
再次编辑
考虑一下,您可以向析构函数添加代码,调用另一个静态方法来递减计数器:D
其他提示
如果您只增加一个数字,则有一个特殊的类(Interlocked)用于此目的......
http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx
互锁增量法
以原子操作的形式递增指定变量并存储结果。
System.Threading.Interlocked.Increment(myField);
有关线程最佳实践的更多信息...
我猜这是针对单例模式或类似的东西。你想要做的不是锁定你的对象,而是在修改它时锁定计数器。
private static int counter = 0;
private static object counterLock = new Object();
lock(counterLock) {
counter++;
myCounter = counter;
}
因为您当前的代码有点多余。特别是在构造函数中,只有一个线程可以调用构造函数,这与可以跨线程共享并从任何共享线程访问的方法不同。
从我可以从您的代码中得知的一点来看,您正在尝试为对象提供创建时的当前计数。因此,使用上面的代码,当计数器在本地更新和设置时,计数器将被锁定。因此所有其他构造函数都必须等待计数器被释放。
您可以使用另一个静态对象来锁定它。
private static Object lockObj = new Object();
并在构造函数中锁定该对象。
lock(lockObj){}
但是,我不确定是否存在因编译器优化而应处理的情况 .NET
就像java的情况一样
最有效的方法是使用互锁增量操作。它将递增计数器并立即返回静态计数器的新设置值(原子地)
class MyClass {
static int _LastInstanceId = 0;
private readonly int instanceId;
public MyClass() {
this.instanceId = Interlocked.Increment(ref _LastInstanceId);
}
}
在您的原始示例中,lock(this) 语句不会达到预期的效果,因为每个单独的实例都将具有不同的“this”引用,因此多个实例可能会同时更新静态成员。
从某种意义上说,构造函数可以被认为是线程安全的,因为在构造函数完成之前,对正在构造的对象的引用是不可见的,但这对于保护静态变量没有任何好处。
(迈克·沙尔(Mike Schall)首先拥有互锁位)
我想如果你修改 单例模式 包含一个计数(显然使用线程安全方法),你会没事的:)
编辑
糟糕,我不小心删除了!
我不确定实例构造函数是否 是 线程安全,我记得在一本设计模式书中读到过这一点,你需要确保在实例化过程中锁到位,纯粹是因为这个..
@抢
仅供参考,这个类可能不是单例,我需要访问不同的实例。他们必须简单地进行计数。您将更改单例模式的哪一部分来执行“计数器”递增?
或者您是否建议我公开一个静态方法来构建阻止对递增并用锁读取计数器的代码的访问。
public MyClass {
private static Int32 counter = 0;
public static MyClass GetAnInstance() {
lock(MyClass) {
counter++;
return new MyClass();
}
}
private Int32 myCount;
private MyClass() {
myCount = counter;
}
}