문제

내가 제기되고 믿는 경우 여러 스레드에서 액세스할 수 있는 변수,다음 모두에서 읽고 쓰는 변수에 의해 보호되어야한 동기화와 같은 코드는"lock"문의하기 때문에,프로세서도 다른 스레드로 전환을 통해서 중간에 쓰.

그러나이었을 통해 보는 시스템입니다.웹.보안입니다.회원이 사용하는 반사체고 이와 같은 코드:

public static class Membership
{
    private static bool s_Initialized = false;
    private static object s_lock = new object();
    private static MembershipProvider s_Provider;

    public static MembershipProvider Provider
    {
        get
        {
            Initialize();
            return s_Provider;
        }
    }

    private static void Initialize()
    {
        if (s_Initialized)
            return;

        lock(s_lock)
        {
            if (s_Initialized)
                return;

            // Perform initialization...
            s_Initialized = true;
        }
    }
}

왜 s_Initialized 필드를 읽을 외부의 자물쇠?할 수 없는 또 다른 스레드 수를 쓰려고 하고 그것에 동일한 시간입니까? 은 읽기와 쓰기의 변수는 원자?

도움이 되었습니까?

해결책

에 대한 결정적인 응답 가 spec.:)

파티션이 나는 절 12.6.6 의 CLI 사양 상태:"따르는 CLI 것을 보장하는 읽기 및 쓰기 권한이 제대로 정렬 메모리보다 크지 않은 기본 단어의 크기는 원자를 때는 모든 쓸에 액세스하는 위치는 같은 크기입니다."

도록 확인하는 s_Initialized 없을 것이 불안정,그리고 읽고 쓰 primitve 유형보다 더 작은 32 비트는 원자.

특히, doublelong (Int64UInt64 용) 원자성을 보장하에서 32 비트 플랫폼입니다.사용할 수 있는 방법에 대 Interlocked 클래스를 보호한다.

또한,면서 읽고 쓰는 원자,경쟁 조건으로,빼기,그리고 증가시키고 감소시키는 기본 형식 때문에,그들이 읽어야 합니다,운영에서,그리고 다시 작성됩니다.Interlocked 클래스를 보호할 수 있습을 사용하여 이러한 CompareExchangeIncrement 방법이 있습니다.

맞물리는 메모리를 생성을 방지하기 위해 장벽 프로세서에서 순서를 읽고 씁니다.잠금을 만듭에만 필요한 장벽에서 이 예입니다.

다른 팁

이것은(나)형태의 이중 확인 잠그는 패턴 스레드에 안전한 C#!

거기에 하나 큰 문제에 이 코드:

s_Initialized 지 않는 휘발성이다.즉,기록 초기화 코드에서 후 이동할 수 있습니다 s_Initialized 은 설정을 사실과 다른 스레드에 볼 수 있는 초기화되지 않은 코드를 경우에도 s_Initialized 에 대한 사실입니다.이 적용되지 않 Microsoft 의 구현 프레임워크의 모든 쓸 휘발성 쓰기.

그러나 또한 마이크로 소프트의 구현을 읽의 데이터 초기화되지 않은 다시 주문하실 수 있습니다(i.e프리패치된 cpu),그래서 당 s_Initialized 사실,데이터를 읽어야하는 초기화할 수 있는 결과를 읽고 예전에,초기화되지 않았기 때문에 데이터의 cache-타(ie.읽기 순서를 바꾸).

예를 들어:

Thread 1 reads s_Provider (which is null)  
Thread 2 initializes the data  
Thread 2 sets s\_Initialized to true  
Thread 1 reads s\_Initialized (which is true now)  
Thread 1 uses the previously read Provider and gets a NullReferenceException

이동 읽의 s_Provider 읽기 전에 s_Initialized 완벽하게 합법적이기 때문에 없는 휘발성을 읽다.

는 경우 s_Initialized 것은 휘발성,읽기의 s_Provider 것을 이동할 수 있도록 허용되지 않습 읽기 전에 s_Initialized 고 또한 초기화의 공급자가 허용하지 않는 이동 후 s_Initialized true 로 설정하면 모든 것이 지금은 괜찮습니다.

조 Duffy 또한 기사를 썼 이 문제에 관하여: 깨 개에서 더블 잠금 확인

걸린 겁니다 질문은 제목에서 확실히지 않는 진짜 문제는 리다.

직함 질문은 간단한 대답이"No"-그러나 이것은 아무 도움도 모두에서,당신은 진짜 문제는 생각하지 않는 사람은 주어진 간단한 대답이다.

진짜 문제는 로 요청을 제시 많은 나중에 더 관련 그는 예입니다.

왜 s_Initialized 필드 읽기 외부의 자물쇠?

이 대답은 간단합니다,하지만 전혀 상관없이의 자성 변수에 액세스합니다.

이 s_Initialized 필드를 읽기의 외부기 때문에 잠금 잠금 비싼.

이후 s_Initialized 필드를 근본적으로"작성되면"그것은 반환하지 않는다.

그것은 경제적이 그것을 읽을 밖에서 잠급니다.

저렴한 비용 으로 활동 의 기회를 갖는 혜택입니다.

그 이유는 그것을 읽는 외부의 잠금을 지불을 피하기 위해 비용을 사용하여 잠금하지 않는 한 그것의 표시됩니다.

면 잠금 저렴한 코드를 간단하게 될 것이며,생략는 먼저 확인합니다.

(편집:좋은 응답에서 로 다음과 같습니다.예치,부울 값을 읽는 매우 많은 유념해야 합니다.만약 누군가가 건설 프로세서와 비-원자 부울 값 읽고,그들이 확인하 DailyWTF.)

올바른 답을 것 같다,"예,대부분."

  1. 요한의 답변을 참조하 CLI 사양을 나타내는 접근하는 변수보다 더 큰 32 비트에서 32 비트 프로세서는 원자.
  2. 더 확인서는 C#사양,절 5.5, 자성의 변수를 참조:

    읽기 및 쓰기에 다음과 같은 데이터의 유형은 원자:bool,char,byte,sbyte,짧은,ushort,uint,int,float,그리고 참조 형식입니다.또한,읽기와 쓰기의 열거형으로 근본적인 유형에 이 목록은 또한 원자.읽기와 쓰기의 다른 유형을 포함하여,긴,ulong,더블,그리고 소수뿐만 아니라,사용자 정의 형식을 보장하지는 않습 atomic.

  3. 코드 내에서 예 의역에서 멤버십 클래스에 의해 작성된 ASP.NET 팀들,그래서 그것은 항상 안전하다고 가정하는 방법에 액세스하 s_Initialized 분야 올바른 것입니다.이제 우리는 이유를 모르겠어.

편집:스 Danecker 점도의 액세스 필드는 원자,s_Initialized 정말로 표시 휘발성 하는지 확인 잠그는 부서지지 않 프로세서에 의해 다시 정렬해 읽기 및 쓰기.

초기화 기능을 결함이 있습니다.야 합니다 더 많은 다음과 같다:

private static void Initialize()
{
    if(s_initialized)
        return;

    lock(s_lock)
    {
        if(s_Initialized)
            return;
        s_Initialized = true;
    }
}

지 않고 두 번째는 체크 내부 잠금 그의 가능한 초기화 코드 두 번 실행됩니다.그래서 첫 번째 확인은 성능을 저장 당신은 잠을 불필요하게,그리고 두 번째 확인하는 경우에는 스레드가 실행하면 초기화 코드 그러나지 않았음 설정 s_Initialized 깃발하고 그 두 번째 스레드가 전 검사 및 기다리고 있다.

읽기와 쓰기의 변하지 않은 유념해야 합니다.당신을 사용할 필요가 동기화 Api 를 에뮬레이션 원자 읽기/쓰기.

에 대한 최고의 참조 이와 많은 문제와 동시성 확인,당신의 사본을 잡고 조 Duffy 의 최신 광경.그것은 리퍼!

"에 액세스하는 변수에 C#원자 조작?"

Nope.그리고 그것은 C#을 일으며,심지어.순 것,그것의 프로세서를 것입니다.

OJ 은 자리에서는 조 Duffy 은 사람을 가을의 종류에 대한 정보입니다.그리고"연동"한 검색어를 사용하고자하는 경우 자세한 내용을 알고 있습니다.

"찢어진 읽기"에서 발생할 수 있습니다 어떤 값 필드를 추가하보다 더 크기의 포인터이다.

@레온
나는 당신의 지점 방법을 요청 했습니다 다음 주석에서 질문할 수 있도록 촬영에서의 몇 가지 다른 방법이 있습니다.

명확하게 알고 싶어하는 경우 안전이 동시에 스레드 읽기 및 쓰기를 부울 필드 없이 명시적 동기화 코드,i.e.,에 액세스하는 부울(또는 다른 원시-입력)변하기 쉬운 원자.

나는 그때 사용한 회원 코드는 구체적인 예를 들어,하지만 소개하는 무리를 주의 산만,같은 더블 잠금 확인,사실 s_Initialized 만 한 번 설정하고,내가 주석으로 초기화합니다.

내 나쁘다.

당신은 또한 장식 s_Initialized 로 volatile 키워드는 고용의 잠급니다.

는 올바르지 않습니다.당신은 여전히 발생하고 문제의 두 번째 스레드에 전달하기 전에 확인 첫 번째 스레드에 할 수있는 기회를했을 설정하는 국기는 것이 결과에 여러 번 실행 초기화 코드입니다.

나는 당신이 요구 하는 경우 s_Initialized 수는 불안정한 상태에있을 때 읽을 밖에서 잠급니다.짧은 대답은 아니다.간단한 과제/읽기 끓여 하나의 어셈블리에 지시하는 원자에서 모든 프로세서 나는 생각할 수 있습니다.

나는 확실하지 않는 경우는 지정을 위한 64 비트로 변수에 따라 달라집니다,프로세서,고 싶다고 가정하지 않은 것 원자지만 그것은 아마 이에 현대적인 32 비트 프로세서와는 확실히 모든 64 비트 프로세서를 지원합니다.당의 복잡한 값을 유지 않을 것이 원자.

나는 그들이 나의 확실하지 않은 지점의 자물쇠에 들지 않는 한 당신은 또한 무언가를 하고 있 s_Provider 동시에 다음의 잠금을 수 있도록 할 것이라 이러한 호출이 일어났다.

//Perform initialization 댓글을 만들 s_Provider?에 대한 인스턴스

private static void Initialize()
{
    if (s_Initialized)
        return;

    lock(s_lock)
    {
        s_Provider = new MembershipProvider ( ... )
        s_Initialized = true;
    }
}

그렇지 않으면는 정적 재산을 얻을 그냥 돌려보내기 위하여 려고 하고 있는 null anyway.

연동 을 제공합니다.고 그렇지 않으면 내가 매우 좋습니다.

내가 짐작하는 것들이지 않은 원자.

귀하의 코드를 항상 작업에 약하게 정렬 아키텍처,해야 합 MemoryBarrier 를 작성하기 전에 s_Initialized.

s_Provider = new MemershipProvider;

// MUST PUT BARRIER HERE to make sure the memory writes from the assignment
// and the constructor have been wriitten to memory
// BEFORE the write to s_Initialized!
Thread.MemoryBarrier();

// Now that we've guaranteed that the writes above
// will be globally first, set the flag
s_Initialized = true;

메모리 쓰기에서 일어나는 MembershipProvider 생성자와 쓰기 s_Provider 를 하기 전에 당신이 쓰 s_Initialized 에 약하게 주문 프로세서입니다.

많은 생각이 스레이는 여부에 대해 뭔가가 원하지 않습니다.는 문제입니다.문제입 순서는 스레드의 기록은 다른 스레드 표시.에 약하게 정렬 아키텍처,기록하는 메모리를 발생하지 않기 위해서는 진짜 문제는지 여부 변수에 맞는 데이터 버스입니다.

편집: 실제로,나는 믹싱 플랫폼에 내가 문이 있습니다.C#CLR spec 필요로 하는 기록은 전 세계적으로 보이는 순서대로(사용하여 비싼 저장소 지침에 대한 모든 저장소 필요한 경우).따라서,당신은 필요 없어요 실제로 그 메모리 장벽이있다.그러나,경우에는 C 또는 C++는 같은 보증의 글로벌 표시기 위해 존재하고,귀하의 타겟 플랫폼 수도가 약하게 주 메모리,그리고는 다중쓰레드 방식을,당신은 것이 있는지 확인해야 생성자를 쓰는 전 세계적으로 가시기 전에 당신 업데이트 s_Initialized,테스트 밖에서 잠급니다.

If (itisso) { 체크에는 부울은 원하지만,되지 않은 경우에도 할 필요가 없을 잠그려면 먼저 확인합니다.

는 경우,모든 쓰레드가 완료되면 초기화한 다음 그것을 것이 사실이다.그것이 문제가되지 않는 경우 여러 쓰레드는 한번에.그들은 모두 같은 대답하고,없을 것이다.

두 번째는 체크 내부에 자물쇠가 필요하기 때문에 다른 스레드 수를 잡고 있는 잠금 먼저 완료 초기화 프로세스를 이미입니다.

무엇을 할지 묻는 필드 액세스 방법에는 여러 번의 원자--하는 대답이 아니다.

위의 예에서의 초기화 루틴 결함 발생할 수 있습니다 여러 초기 설정입니다.해야 합 확인 s_Initialized 내부 플래그 잠금뿐만 아니라 외부을 방지하기 위해,경쟁 조건에서는 여러 쓰레드를 읽 s_Initialized 국기 전에 그들의 실제적으로 초기화 코드입니다.E.g.,

private static void Initialize()
{
    if (s_Initialized)
        return;

    lock(s_lock)
    {
        if (s_Initialized)
            return;
        s_Provider = new MembershipProvider ( ... )
        s_Initialized = true;
    }
}

Ack,갤...로 지적했다,이것은 참으로 잘못되었습니다.그렇지 않을 방지하는 두 번째 스레드에 들어가는"초기화"코드는 섹션입니다.Bah.

당신은 또한 장식 s_Initialized 로 volatile 키워드는 고용의 잠급니다.

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