인스턴스 생성자는 정적 멤버를 설정합니다. 스레드로부터 안전합니까?

StackOverflow https://stackoverflow.com/questions/41792

문제

일부 코드를 리팩터링 중이며 사용 방법이 궁금합니다. lock 인스턴스 생성자에서.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

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

확인 해주세요

  1. 인스턴스 생성자는 스레드로부터 안전합니다.
  2. lock 문은 정적 'counter' 멤버가 아닌 해당 코드 블록에 대한 액세스를 방지합니다.

원래 프로그래머의 의도가 각 인스턴스가 자신의 '카운트'를 알도록 하는 것이라면 다른 스레드가 새로운 스레드가 되지 않도록 '카운터' 멤버에 대한 액세스를 어떻게 동기화합니까? MyClass 이것이 카운트를 설정하기 전에 카운트를 변경합니까?

참고 - 이 클래스는 싱글톤이 아닙니다.인스턴스는 자신의 번호만 알고 있어야 합니다.

도움이 되었습니까?

해결책

@ajmastrean

싱글톤 패턴 자체를 사용해야 한다는 것이 아니라 인스턴스화 프로세스를 캡슐화하는 방법을 채택해야 한다는 뜻입니다.

즉.

  • 생성자를 비공개로 만듭니다.
  • 형식을 반환하는 정적 인스턴스 메서드를 만듭니다.
  • 정적 인스턴스 메서드에서는 인스턴스화하기 전에 lock 키워드를 사용합니다.
  • 해당 유형의 새 인스턴스를 인스턴스화합니다.
  • 개수를 늘립니다.
  • 새 인스턴스를 잠금 해제하고 반환합니다.

편집하다

나에게 발생한 한 가지 문제는 카운트가 언제 감소했는지 어떻게 알 수 있습니까?;)

다시 편집

생각해 보면, 카운터를 감소시키기 위해 또 다른 정적 메서드를 호출하는 소멸자에 코드를 추가할 수 있습니다.

다른 팁

숫자만 증가시키는 경우 이를 위한 특수 클래스(Interlocked)가 있습니다.

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

Interlocked.Increment 메서드

원자성 연산으로 지정된 변수를 증가시키고 결과를 저장합니다.

System.Threading.Interlocked.Increment(myField);

스레딩 모범 사례에 대한 추가 정보...

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

나는 이것이 싱글톤 패턴이나 그와 유사한 것이라고 생각합니다.당신이 원하는 것은 개체를 잠그는 것이 아니라 개체를 수정하는 동안 카운터를 잠그는 것입니다.

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 자바의 경우처럼

이를 수행하는 가장 효율적인 방법은 Interlocked 증분 연산을 사용하는 것입니다.카운터를 증가시키고 새로 설정된 정적 카운터 값을 한 번에 (원자적으로) 반환합니다.

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

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

원래 예제에서는 각 개별 인스턴스가 서로 다른 "this" 참조를 갖고 여러 인스턴스가 동시에 정적 멤버를 업데이트할 수 있기 때문에 lock(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;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top