实例构造函数设置了一个静态成员,线程安全吗?

时间:2020-03-05 18:47:04  来源:igfitidea点击:

我正在重构一些代码,并且想知道实例构造函数中使用lock

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

请确认

  • 实例构造函数是线程安全的。
  • lock语句阻止访问该代码块,而不是静态" counter"成员。

如果原始程序员的意图是让每个实例知道其"计数",那么我将如何同步对"计数器"成员的访问,以确保另一个线程不是新的" MyClass"并在此之前更改计数一盘算一下?

仅供参考,该课程不是单身人士。实例必须仅知道其编号。

解决方案

回答

我猜这是为单例模式或者类似的东西。我们要执行的操作不是锁定对象,而是在修改计数器时锁定它。

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)

回答

我认为,如果我们修改单例模式以包括计数(显然使用线程安全方法),则可以:)

编辑

废话我不小心删除了!

我不确定实例构造函数是否是线程安全的,我记得在设计模式书中读过有关此内容的文章,我们仅出于此原因就需要确保在实例化过程中锁定到位。

回答

@抢

仅供参考,此类可能不是单例,我需要访问其他实例。他们必须简单地保持计数。我们将更改单例模式的哪一部分以执行"计数器"增量?

或者,我们是否建议我公开一个静态方法来构造访问,以阻止访问递增且带锁的计数器的代码。

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}

回答

@ajmastrean

我并不是说我们应该使用单例模式本身,而是采用其封装实例化过程的方法。

IE。

  • 将构造函数设为私有。
  • 创建一个返回该类型的静态实例方法。
  • 在静态实例方法中,在实例化之前使用lock关键字。
  • 实例化该类型的新实例。
  • 增加计数。
  • 解锁并返回新实例。

编辑

我想过的一个问题是,如果我们怎么知道什么时候计数下降了? ;)

再次编辑

考虑一下,我们可以将代码添加到析构函数中,该析构函数调用另一个静态方法来减少计数器:D

回答

如果只增加一个数字,那么有一个特殊的类(互锁)...

http://msdn.microsoft.com/zh-CN/library/system.threading.interlocked.increment.aspx

Interlocked.Increment Method
  
  Increments a specified variable and stores the result, as an atomic operation.
System.Threading.Interlocked.Increment(myField);

有关线程最佳实践的更多信息...

http://msdn.microsoft.com/zh-CN/library/1c9txz50.aspx

回答

执行此操作的最有效方法是使用"互锁"增量操作。它将增加计数器并一次返回所有静态计数器的新设置值(原子上)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

在原始示例中,lock(this)语句将不会具有预期的效果,因为每个单独的实例将具有不同的" this"引用,因此多个实例可以同时更新静态成员。

从某种意义上说,可以将构造函数视为线程安全的,因为在构造函数完成之前,对所构造对象的引用是不可见的,但这对保护静态变量没有任何好处。

(Mike Schall首先拥有互锁的位)