实例构造函数设置一个静态成员,它是否是线程安全的?

时间:2021-04-18 18:06:34

I am re-factoring some code and am wondering about the use of a lock in the instance constructor.

我正在重新考虑一些代码,并且想知道在实例构造函数中使用锁。

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

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

Please confirm

  1. Instance constructors are thread-safe.
  2. 实例构造函数是线程安全的。

  3. The lock statement prevents access to that code block, not to the static 'counter' member.
  4. lock语句阻止访问该代码块,而不是静态“计数器”成员。

If the intent of the original programmer were to have each instance know its 'count', how would I synchronize access to the 'counter' member to ensure that another thread isn't new'ing a MyClass and changing the count before this one sets its count?

如果原始程序员的意图是让每个实例知道它的'count',我将如何同步访问'counter'成员以确保另一个线程不是新的MyClass并在此之前更改计数它的数量?

FYI - This class is not a singleton. Instances must simply be aware of their number.

仅供参考 - 此课程不是单身人士。实例必须只知道它们的编号。

7 个解决方案

#1


3  

@ajmastrean

I am not saying you should use the singleton pattern itself, but adopt its method of encapsulating the instantiation process.

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

i.e.

  • Make the constructor private.
  • 使构造函数私有。

  • Create a static instance method that returns the type.
  • 创建一个返回类型的静态实例方法。

  • In the static instance method, use the lock keyword before instantiating.
  • 在静态实例方法中,在实例化之前使用lock关键字。

  • Instantiate a new instance of the type.
  • 实例化该类型的新实例。

  • Increment the count.
  • 增加计数。

  • Unlock and return the new instance.
  • 解锁并返回新实例。

EDIT

One problem that has occurred to me, if how would you know when the count has gone down? ;)

我发生了一个问题,如果你怎么知道计数何时下降? ;)

EDIT AGAIN

Thinking about it, you could add code to the destructor that calls another static method to decrement the counter :D

考虑一下,你可以在析构函数中添加代码,调用另一个静态方法来递减计数器:D

#2


12  

If you are only incrementing a number, there is a special class (Interlocked) for just that...

如果你只是增加一个数字,那就有一个特殊的类(Interlocked)就是这样...

http://msdn.microsoft.com/en-us/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);

More information about threading best practices...

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

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

#3


4  

I'm guessing this is for a singleton pattern or something like it. What you want to do is not lock your object, but lock the counter while your are modifying it.

我猜这是针对单身模式或类似的东西。你想要做的不是锁定你的对象,而是在修改它时锁定计数器。

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

Because your current code is sort of redundant. Especially being in the constructor where there is only one thread that can call a constructor, unlike with methods where it could be shared across threads and be accessed from any thread that is shared.

因为您当前的代码有点多余。特别是在构造函数中,只有一个线程可以调用构造函数,这与可以跨线程共享并可以从任何共享线程访问的方法不同。

From the little I can tell from you code, you are trying to give the object the current count at the time of it being created. So with the above code the counter will be locked while the counter is updated and set locally. So all other constructors will have to wait for the counter to be released.

从我可以告诉您的代码中,您试图在创建对象时为对象提供当前计数。因此,使用上面的代码,计数器将被锁定,而计数器将在本地更新和设置。所以其他所有构造函数都必须等待计数器被释放。

#4


3  

You can use another static object to lock on it.

您可以使用另一个静态对象来锁定它。

private static Object lockObj = new Object();

and lock this object in the constructor.

并在构造函数中锁定此对象。

lock(lockObj){}

However, I'm not sure if there are situations that should be handled because of compiler optimization in .NET like in the case of java

但是,我不确定是否存在因.NET中的编译器优化而应该处理的情况,例如java的情况

#5


2  

The most efficient way to do this would be to use the Interlocked increment operation. It will increment the counter and return the newly set value of the static counter all at once (atomically)

最有效的方法是使用Interlocked增量操作。它将递增计数器并立即返回静态计数器的新设置值(原子地)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

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

In your original example, the lock(this) statement will not have the desired effect because each individual instance will have a different "this" reference, and multiple instances could thus be updating the static member at the same time.

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

In a sense, constructors can be considered to be thread safe because the reference to the object being constructed is not visible until the constructor has completed, but this doesn't do any good for protecting a static variable.

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

(Mike Schall had the interlocked bit first)

(Mike Schall先把互锁位置)

#6


0  

I think if you modify the Singleton Pattern to include a count (obviously using the thread-safe method), you will be fine :)

我想如果你修改Singleton模式以包含一个计数(显然使用线程安全的方法),你会没事的:)

Edit

Crap I accidentally deleted!

废话我不小心删了!

I am not sure if instance constructors ARE thread safe, I remember reading about this in a design patterns book, you need to ensure that locks are in place during the instantiation process, purely because of this..

我不确定实例构造函数是否是线程安全的,我记得在设计模式书中读到这个,你需要确保在实例化过程中锁定到位,纯粹是因为这个..

#7


0  

@Rob

FYI, This class may not be a singleton, I need access to different instances. They must simply maintain a count. What part of the singleton pattern would you change to perform 'counter' incrementing?

仅供参考,此类可能不是单身,我需要访问不同的实例。他们必须简单地维持一个计数。您将改变单身模式的哪一部分来执行“计数器”递增?

Or are you suggesting that I expose a static method for construction blocking access to the code that increments and reads the counter with a lock.

或者你是否建议我公开一个静态方法来构造阻塞对代码的访问,该代码增加并用锁读取计数器。

public MyClass {

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

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

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

#1


3  

@ajmastrean

I am not saying you should use the singleton pattern itself, but adopt its method of encapsulating the instantiation process.

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

i.e.

  • Make the constructor private.
  • 使构造函数私有。

  • Create a static instance method that returns the type.
  • 创建一个返回类型的静态实例方法。

  • In the static instance method, use the lock keyword before instantiating.
  • 在静态实例方法中,在实例化之前使用lock关键字。

  • Instantiate a new instance of the type.
  • 实例化该类型的新实例。

  • Increment the count.
  • 增加计数。

  • Unlock and return the new instance.
  • 解锁并返回新实例。

EDIT

One problem that has occurred to me, if how would you know when the count has gone down? ;)

我发生了一个问题,如果你怎么知道计数何时下降? ;)

EDIT AGAIN

Thinking about it, you could add code to the destructor that calls another static method to decrement the counter :D

考虑一下,你可以在析构函数中添加代码,调用另一个静态方法来递减计数器:D

#2


12  

If you are only incrementing a number, there is a special class (Interlocked) for just that...

如果你只是增加一个数字,那就有一个特殊的类(Interlocked)就是这样...

http://msdn.microsoft.com/en-us/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);

More information about threading best practices...

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

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

#3


4  

I'm guessing this is for a singleton pattern or something like it. What you want to do is not lock your object, but lock the counter while your are modifying it.

我猜这是针对单身模式或类似的东西。你想要做的不是锁定你的对象,而是在修改它时锁定计数器。

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

Because your current code is sort of redundant. Especially being in the constructor where there is only one thread that can call a constructor, unlike with methods where it could be shared across threads and be accessed from any thread that is shared.

因为您当前的代码有点多余。特别是在构造函数中,只有一个线程可以调用构造函数,这与可以跨线程共享并可以从任何共享线程访问的方法不同。

From the little I can tell from you code, you are trying to give the object the current count at the time of it being created. So with the above code the counter will be locked while the counter is updated and set locally. So all other constructors will have to wait for the counter to be released.

从我可以告诉您的代码中,您试图在创建对象时为对象提供当前计数。因此,使用上面的代码,计数器将被锁定,而计数器将在本地更新和设置。所以其他所有构造函数都必须等待计数器被释放。

#4


3  

You can use another static object to lock on it.

您可以使用另一个静态对象来锁定它。

private static Object lockObj = new Object();

and lock this object in the constructor.

并在构造函数中锁定此对象。

lock(lockObj){}

However, I'm not sure if there are situations that should be handled because of compiler optimization in .NET like in the case of java

但是,我不确定是否存在因.NET中的编译器优化而应该处理的情况,例如java的情况

#5


2  

The most efficient way to do this would be to use the Interlocked increment operation. It will increment the counter and return the newly set value of the static counter all at once (atomically)

最有效的方法是使用Interlocked增量操作。它将递增计数器并立即返回静态计数器的新设置值(原子地)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

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

In your original example, the lock(this) statement will not have the desired effect because each individual instance will have a different "this" reference, and multiple instances could thus be updating the static member at the same time.

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

In a sense, constructors can be considered to be thread safe because the reference to the object being constructed is not visible until the constructor has completed, but this doesn't do any good for protecting a static variable.

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

(Mike Schall had the interlocked bit first)

(Mike Schall先把互锁位置)

#6


0  

I think if you modify the Singleton Pattern to include a count (obviously using the thread-safe method), you will be fine :)

我想如果你修改Singleton模式以包含一个计数(显然使用线程安全的方法),你会没事的:)

Edit

Crap I accidentally deleted!

废话我不小心删了!

I am not sure if instance constructors ARE thread safe, I remember reading about this in a design patterns book, you need to ensure that locks are in place during the instantiation process, purely because of this..

我不确定实例构造函数是否是线程安全的,我记得在设计模式书中读到这个,你需要确保在实例化过程中锁定到位,纯粹是因为这个..

#7


0  

@Rob

FYI, This class may not be a singleton, I need access to different instances. They must simply maintain a count. What part of the singleton pattern would you change to perform 'counter' incrementing?

仅供参考,此类可能不是单身,我需要访问不同的实例。他们必须简单地维持一个计数。您将改变单身模式的哪一部分来执行“计数器”递增?

Or are you suggesting that I expose a static method for construction blocking access to the code that increments and reads the counter with a lock.

或者你是否建议我公开一个静态方法来构造阻塞对代码的访问,该代码增加并用锁读取计数器。

public MyClass {

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

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

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