문제

여러 클래스에서 사용하는 C# 싱글톤 클래스가 있습니다.다음을 통해 액세스할 수 있나요? Instance ~로 Toggle() 메소드 스레드로부터 안전합니까?그렇다면 어떤 가정, 규칙 등에 따라그렇지 않다면 왜? 그리고 어떻게 고칠 수 있나요?

public class MyClass
{
    private static readonly MyClass instance = new MyClass();

    public static MyClass Instance
    {
        get { return instance; }
    }

    private int value = 0;

    public int Toggle()
    {
        if(value == 0) 
        {
            value = 1; 
        }
        else if(value == 1) 
        { 
            value = 0; 
        }

        return value;
    }
}
도움이 되었습니까?

해결책

'인스턴스'를 통해 'Toggle()' 클래스에 대한 액세스가 스레드로부터 안전합니까?그렇다면 어떤 가정, 규칙 등에 따라그렇지 않다면 왜, 어떻게 해결할 수 있습니까?

아니요, 스레드로부터 안전하지 않습니다.

기본적으로 두 스레드 모두 다음을 실행할 수 있습니다. Toggle 동시에 작동하므로 이런 일이 발생할 수 있습니다.

    // thread 1 is running this code
    if(value == 0) 
    {
        value = 1; 
        // RIGHT NOW, thread 2 steps in.
        // It sees value as 1, so runs the other branch, and changes it to 0
        // This causes your method to return 0 even though you actually want 1
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    return value;

다음 가정을 가지고 작업해야 합니다.

2개의 스레드가 실행 중인 경우 어느 지점에서든 무작위로 서로 인터리브하고 상호 작용할 수 있습니다.64비트 정수 또는 부동소수점(32비트 CPU의 경우)을 쓰거나 읽는 작업을 중간쯤 완료하면 다른 스레드가 뛰어들어 아래에서 이를 변경할 수 있습니다.

두 스레드가 공통된 항목에 액세스하지 않는 경우에는 문제가 되지 않지만 액세스하자마자 서로 발가락을 밟는 것을 방지해야 합니다..NET에서 이를 수행하는 방법은 잠금을 사용하는 것입니다.

다음과 같은 사항을 생각하여 잠글 항목과 위치를 결정할 수 있습니다.

특정 코드 블록에 대해 다음 값이 있는 경우 something 내 밑에서 옷을 갈아입었는데, 그게 문제가 될까?그럴거면 잠가야지 something 중요한 코드 기간 동안.

당신의 예를 다시 보면

    // we read value here
    if(value == 0) 
    {
        value = 1; 
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    // and we return it here
    return value;

이것이 우리가 기대하는 것을 반환하기 위해 우리는 다음과 같이 가정합니다. value 읽기와 읽기 사이에는 변경되지 않습니다. return.이 가정이 실제로 정확하려면 다음을 잠가야 합니다. value 해당 코드 블록 기간 동안.

그래서 당신은 이렇게 할 것입니다 :

lock( value )
{
     if(value == 0) 
     ... // all your code here
     return value;
}

하지만

.NET에서는 참조 유형만 잠글 수 있습니다.Int32는 값 유형이므로 잠글 수 없습니다.
우리는 '더미' 객체를 도입하고 잠금으로써 이 문제를 해결합니다. 저것 '가치'를 잠그고 싶은 곳이면 어디든지요.

이것이 바로 벤 샤이어먼 을 지칭하고 있습니다.

다른 팁

Ben이 지적했듯이 원래 구현은 스레드로부터 안전하지 않습니다.

스레드로부터 안전하게 만드는 간단한 방법은 잠금 문을 도입하는 것입니다.예.이와 같이:

public class MyClass
{
    private Object thisLock = new Object();
    private static readonly MyClass instance = new MyClass();
    public static MyClass Instance
    {
        get { return instance; }
    }
    private Int32 value = 0;
    public Int32 Toggle()
    {
        lock(thisLock)
        {
            if(value == 0) 
            {
                value = 1; 
            }
            else if(value == 1) 
            { 
                value = 0; 
            }
            return value;
        }
    }
}

나는 그렇게 생각했다.하지만 세부 사항을 찾고 있습니다 ...'Toggle ()'는 정적 방법이 아니지만 정적 속성의 구성원입니다 ( '인스턴스'를 사용할 때).그게 스레드 사이에서 공유하는 이유는 무엇입니까?

애플리케이션이 다중 스레드이고 여러 스레드가 해당 메소드에 액세스할 것으로 예상할 수 있는 경우 스레드 간에 공유됩니다.클래스가 싱글톤이기 때문에 다른 스레드가 SAME 객체에 액세스한다는 것을 알고 있으므로 메서드의 스레드 안전성에 주의하세요.

그리고 이것은 일반적으로 싱글 톤에 어떻게 적용됩니까?수업의 모든 방법에서 이것을 해결해야합니까?

위에서 말했듯이, 이는 싱글톤이기 때문에 다른 스레드가 동일한 개체에, 아마도 동시에 액세스할 수 있다는 것을 알고 있습니다.이는 모든 메소드가 잠금을 획득하도록 해야 한다는 의미는 아닙니다.동시 호출로 인해 클래스 상태가 손상될 수 있다는 것을 알게 되면 @Thomas에서 언급한 방법을 적용해야 합니다.

싱글톤 패턴이 스레드로부터 안전한 내 클래스를 일반 정적 멤버의 모든 스레드 문제에 노출시킨다고 가정할 수 있습니까?

아니요.귀하의 클래스는 단순히 스레드로부터 안전하지 않습니다.싱글톤은 그것과 아무 관련이 없습니다.

(정적 객체에서 호출된 인스턴스 멤버가 스레딩 문제를 일으킨다는 사실에 대해 고민 중입니다.)

그것과도 관련이 없습니다.

다음과 같이 생각해야 합니다.내 프로그램에서 2개 이상의 스레드가 동시에 이 데이터에 액세스할 수 있습니까?

싱글톤 또는 정적 변수를 통해 데이터를 얻거나 객체를 메서드 매개 변수로 전달한다는 사실은 중요하지 않습니다.결국 그것은 PC RAM의 일부 비트와 바이트일 뿐이며 중요한 것은 여러 스레드가 동일한 비트를 볼 수 있는지 여부입니다.

스레드가 해당 메서드 중간에 중지되고 제어권을 다른 스레드로 전송할 수 있습니다.해당 코드 주변에 중요한 섹션이 필요합니다.

private static object _lockDummy = new object();


...

lock(_lockDummy)
{
   //do stuff
}

또한 컴파일러가 공개 기본 생성자를 생성하지 못하도록 MyClass에 보호된 생성자를 추가했습니다.

싱글턴 패턴을 버리고 모든 사람이 클래스의 새 인스턴스를 얻도록 하면 문제가 완화될 것이라고 생각했습니다...하지만 다른 사람이 해당 유형의 정적 개체를 초기화하고 전달하는 것을 막지는 못합니다.또는 여러 스레드를 회전시켜 모두 동일한 인스턴스에서 'Toggle()'에 액세스합니다.

빙고 :-)

이제 이해가된다.힘든 세상이에요.레거시 코드를 리팩토링하지 않았으면 좋겠어요 :(

불행히도, 멀티 스레딩은 어렵고 사물에 대해 매우 편집증이어야합니다 .-)이 경우 가장 간단한 솔루션은 싱글 톤을 고수하고 예제와 같이 값 주위에 잠금을 추가하는 것입니다.

사실 저는 C#을 잘 모릅니다...하지만 저는 Java에 능숙하므로 그에 대한 답변을 제공할 것입니다. 두 가지가 충분히 유사하여 유용할 수 있기를 바랍니다.그렇지 않다면 사과드립니다.

대답은 '아니오'입니다. 안전하지 않습니다.한 스레드는 다른 스레드와 동시에 Toggle()을 호출할 수 있으며, 이 코드에서는 가능성이 낮지만 Thread1이 설정할 수 있는 것이 가능합니다. value Thread2가 이를 확인하는 시간과 설정하는 시간 사이에 있습니다.

문제를 해결하려면 간단히 Toggle()을 만드세요. synchronized.Toggle()을 호출할 수 있는 다른 스레드를 생성할 수 있는 항목을 차단하거나 호출하지 않으므로 저장하기만 하면 됩니다.

인용하다:

if(value == 0) { value = 1; }
if(value == 1) { value = 0; }
return value;

value 항상 0이겠지...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top