2014-12-19 7 views
-1

Interlocked.Exchange을 사용하여 일부 개체 초기화 기능을위한 스레드 안전 잠금 장치를 만들려고합니다. 아래 코드를 고려하십시오. 나는 if이 while을 대체 할 때와 똑같이하고 싶습니다. 내가 물어 보는 이유는 코드가 계속 실행되면서 set 메시지 전에 종료 메시지를받을 때가 있다는 것입니다. 나는 출구에있는 국가가 항상 옳은 것처럼 보이기 때문에 이것은 단지 GUI 일이라는 것을 확인하고 싶다.잠금 장치로 잠금 해제 됨

class Program 
{ 
    private static void Main(string[] args) 
    { 
     Thread thread1 = new Thread(new ThreadStart(() => InterlockedCheck("1"))); 
     Thread thread2 = new Thread(new ThreadStart(() => InterlockedCheck("2"))); 
     Thread thread3 = new Thread(new ThreadStart(() => InterlockedCheck("3"))); 
     Thread thread4 = new Thread(new ThreadStart(() => InterlockedCheck("4"))); 
     thread4.Start(); 
     thread1.Start(); 
     thread2.Start(); 
     thread3.Start(); 

     Console.ReadKey(); 
    } 

    const int NOTCALLED = 0; 
    const int CALLED = 1; 
    static int _state = NOTCALLED; 
    //... 
    static void InterlockedCheck(string thread) 
    { 
     Console.WriteLine("Enter thread [{0}], state [{1}]", thread, _state); 

     //while (Interlocked.Exchange(ref _state, CALLED) == NOTCALLED) 
     if (Interlocked.Exchange(ref _state, CALLED) == NOTCALLED) 
     { 
      Console.WriteLine("Setting state on T[{0}], state[{1}]", thread, _state); 
     } 

     Console.WriteLine("Exit from thread [{0}] state[{1}]", thread, _state); 
    } 
} 

답변

1

난 이후 lock 한 번만 사용될 수 있다는 것을 호출 할 것입니다,하지만 당신은 if 범위 내에서 문을 InterlockedCheck 여러 스레드에서 호출되는 경우에도 정확히 한 번만 실행한다고 가정하면 당신이 올바른지 동시에.

너가 으로 시작하고 원자를 사용하여 CALLED 설정 만하기 때문입니다. 그 첫 번째 호출 만이 NOTCALLED으로 돌아가고 모든 후속 호출은 CALLED이됩니다.

static Lazy<ExpensiveInstance> _lazy = new Lazy<ExpensiveInstance>(Initialization, LazyThreadSafetyMode.ExecutionAndPublication); 

당신은 _lazy.Value과 결과를 검색하고이 _lazy.IsValueCreated으로 만들어 졌는지 여부를 조회 할 수 있습니다

더 나은 (간단) 솔루션은 초기에 적합 닷넷의 Lazy 클래스를 사용하는 것입니다. Initialization은 두 번 이상 필요하기 전까지는 실행되지 않습니다.

0

나는 왜 Interlocked을 더 읽기 쉽고 쉽게 이해할 수있는 lock 성명 대신 여기 사용하고 있지는 않습니다. 개인적으로 나는 후자에게 조언 할 것이다.

어느 쪽이든, "설정"메시지 전에 하나 이상의 "종료"메시지가 나타나는 이유는 스레드 스케줄링 때문입니다. Interlocked에 도달하는 첫 번째 스레드가 항상 "설정"작업을 수행하더라도 해당 스레드는 작업을 수행하기 전에 선점 될 수 있으므로 다른 스레드가 먼저 "exit"메시지를 내보낼 수 있습니다.

정확한 요구에 따라 if을 사용하는 것이 while 루프를 사용하는 것과 동일하지만 원하는 결과를 얻지 못할 가능성이 있습니다. 나는. if을 치는 첫 번째 스레드는 값을 CALLED 값으로 설정하므로 다른 스레드는 계속 진행합니다. 여기에 초기화를 시도하는 경우 다른 스레드가 실제로 초기화 코드를 실행하는 스레드를 기다려야하므로 모두 스레드가 초기화 된 상태가 유효하다는 것을 알 수 있습니다.

내가 생각하기에 좋은 아이디어가 될 것입니다. (즉, 사용자에게 혼란스럽지 않고 실제 코드가 표시 한 것보다 더 복잡하면 올바른 결과를 산출 할 확률이 더 높습니다.) 전체 메서드를 동기화하는 경우.

Interlocked을 사용하면 현재 가지고있는 코드보다 훨씬 복잡합니다. 루프와 적어도 하나의 추가 상태 값이 포함됩니다. 그러나 lock 진술을 사용하면 간단하고 읽기 쉽습니다.그것은 더 같을 것이다 :

const int NOTCALLED = 0; 
const int CALLED = 1; 
static int _state = NOTCALLED; 
static readonly object _lock = new object(); 
//... 
static void InterlockedCheck(string thread) 
{ 
    lock (_lock) 
    { 
     Console.WriteLine("Enter thread [{0}], state [{1}]", thread, _state); 

     if (_state == NOTCALLED) 
     { 
      Console.WriteLine("Setting state on T[{0}], state[{1}]", thread, _state); 
      _state = CALLED; 
     } 

     Console.WriteLine("Exit from thread [{0}] state[{1}]", thread, _state); 
    } 
} 

그 방법을 잠금을 획득 할 수있는 첫 번째 스레드는 다른 스레드 전에 코드의 모든을 실행하기 위해 도착하지, 그리고 그것은 "설정"동작을 보장 특히 첫 번째 스레드는 다른 스레드에서 "exit"연산 전에 발생합니다.

+0

아마도'lock'을 두 번 확인해야합니다. – i3arnon

+0

"'_state' 값을 두 번 확인한다고 가정합니다. 그리고이 구체적인 예에서 그렇게 할 필요는 없습니다. 잠금 장치의 요점은 "enter"및 "exit"메시지의 질서있는 실행을 보장하기 위해 _state 필드의 처리를 보호하는 것입니다. 이중 확인 된 구현은 후자를 수행 할 수 없습니다. 어쨌든, 이중 체크 잠금의 성능 향상은 코드의 복잡성 증가를 정당화하기에 충분하지 않습니다. –

+0

"enter"및 "exit"메시지가 잠금 장치 안에있을 필요는 없습니다. 이러한 메시지를 중요 섹션의 일부로 만드는 것은 가치가 없습니다. 이중 검사는 알려진 패턴이며 패스트 경로의 성능을 향상시킵니다. 영업 이익이 '연동'운영 성과에 대해 묻는 것은 아마도 문제 일 것이라고 생각하십시오. 이중 검사가 너무 복잡한 경우 '지연 성'(빠른 경로에 최적화 됨)이 해당 작업을 수행합니다. – i3arnon