2017-04-13 2 views
1

많은 기사가 말합니다. 긴 작업을 동기화하기 위해 잠금을 사용하지 말고이 규칙을 따르십시오. 그러나 나는 그것에 대해 실제로 그렇게 잘못된 것이 궁금하다. 소중한 자원을 소비하는 것에 관한 것입니까?긴 작업에 C# 잠금을 사용하지 않아야하는 이유는 무엇입니까?

또한 AutoResetEvent를 대신 사용하면 도움이 될까요? 아니면 오랜 시간 작업을 잠그는 것을 잊어서 비동기 프로그래밍을 고려해야합니까?

+0

당신은 내가 포스트는 더 일반적인 질문으로 의미 추측 –

+0

을 달성하기 위해 무엇을하려고하는 몇 가지 코드를 게시하시기 바랍니다. – Moerwald

답변

15

먼저 규칙의 이유를 이해하지 못하면화물 컬트 프로그래밍입니다. vital이 규칙은 맹목적으로 적용하는 것이 아니라 이러한 규칙의 이유를 이해하는 것입니다.

따라서, 두 가지 이유는 짧은 시간에 대한 로크를 보유한다 :

(1) 긴 잠금 동등한 성능 저하. 한 번에 한 사람 만 사용할 수있는 리소스가 있다고 가정 해보십시오. 당신은 정말로 당신의 housemate가 세금을 내고 샤워에서 netflix를 보면서 폭음을 내고 싶습니까? 아니, 다른 사람이 사용할 수 있도록 들어 와서 나가길 원합니다.

(2) long은 동일한 교착 상태를 잠급니다. 오랫동안 자물쇠를 사용하고 있다면 코드를 많이 부르는 것이 좋습니다. 어떤 코드를 호출했는지 모르는 자물쇠 반전이 발생할 확률이 높아집니다.

그래서 표준 조언은 가능한 한 짧은 시간 동안 잠그고 단일 코스 그레인 된 잠금보다는 많은 세분화 된 잠금을 사용하는 것입니다. 물론

, 표준 조언 는 이제 당신이 반전 될 수있는 더 많은 잠금을 가지고 있기 때문에 교착 상태 많은 세밀한 잠금을 또한 증가의 기회를 가지고. 또한 세밀한 자물쇠가 많으면 고려하지 않은 두 개의 고정 된 지역 사이에서 무언가가 실행되는 알려지지 않은 경쟁이 일어날 가능성이 높아집니다. 따라서 표준 조언은 다음과 같습니다. 코드의 큰 부분을 잠그는 작은 수의 거친 잠금을 사용합니다.

표준적인 조언은 모순입니다. 왜 그런가요? 모니터 잠금을 통해 다중 스레드 프로그램에서 공유 메모리를 관리하는 것은 근본적으로 나쁜 생각 인입니다. 그것은 표준적인 관행이지만, 그것이 좋지는 않습니다.

또한 AutoResetEvent를 대신 사용하면 도움이 될까요? 아니면 오랜 시간 작업을 잠그는 것을 잊어서 비동기 프로그래밍을 고려해야합니까?

내가 할 수있는 최고 수준의 조작을 사용하는 것이 좋습니다. 비동기없이 빠져 나갈 수 있다면해라. 그렇지 않은 경우 프로세스 바운드 또는 IO 대기 시간 바인딩 여부에 따라 프로세스 수준 병렬 처리 또는 단일 스레드 비동기를 고려하십시오. 그 이유가 무엇이든간에 작동하지 않는다면 스레드를 가벼운 프로세스로 처리하십시오. 공유 메모리; TPL을 사용하여 스레드를 관리하십시오.

등등. 잠금 장치 또는 상호 연동 된 작업 또는 휘발성 물질을 다루는 것이 최후의 수단이어야하며 이러한 도구는 고급 도구를 만들기위한 기본 요소로 사용해야합니다.

+1

본질은 높은 수준의 추상화, TPL, Concurrent Collections를 사용하며, 더 이상'lock','EventWaitHandle'와 같은 저수준 구조를 사용하지 않으려 고합니다. 추상화는 리소스를 관리하는 데 훨씬 뛰어납니다. –

+0

성능이나 교착 상태가 문제가되지 않는다면 어떻게해야합니까? 어쩌면 자물쇠가 여러 작업을 직렬화하기를 원할 수도 있습니다. 아직도 나쁜가요? 타이머에 의해 N 개의 작업이 트리거되었다고합시다. 시간이 중요하지 않지만 동시에 실행하지 않는 것이 중요합니다. 방금 전역 잠금으로 각 작업을 둘러 쌈으로써 리소스 또는 기타 문제가 발생합니까? 작업에 1 초, 10 초, 10 분이 걸리는 것이 중요합니까? (나는 그것을 해결할 다른 방법이 있음을 안다) – adrianm

+3

@adrianm : 당신의 질문은 : "우리 시나리오에는 문제가 없다"고 말하면 문제가있는 것입니까? 아니요. 성능이나 정확성에 신경 쓰지 않는다면 성능이나 정확성에 문제가없는 것입니다. –

1

간단한 아이디어. Lock은 다른 스레드가 중요 섹션에 들어가는 것을 본질적으로 차단합니다. 임계 영역에 이미 하나의 thread이 있고 다른 작업이 임계 영역에 액세스하기 위해 대기 중입니다. 이렇게하면 해당 물질에있는 threads이 동시에 실행됩니다.그 이유는 당신이 왜 단 하나의 스레드를 사용하지 않을까라는 의문이 제기됩니까?

그래서 첫 번째 이유는 단일 스레드를 실행할 수도 있습니다.

중요한 섹션 긴 작업을 수행하면 잠금 대기중인 다른 모든 스레드도 차단되므로 잠재적으로 코드 실행 시간이 thread만큼 증가합니다.

두 번째 이유는 매우 동시적인 환경에서 코드가 매우 느릴 수 있기 때문입니다.

0

아마도 long locks -> priority inversion에 대한 또 다른 중요한 점이 있습니다 (link 참조). 다음을 고려해 봅시다.

낮은 우선 순위 스레드는 오랫동안 리소스를 잠급니다. 높은 우선 순위 스레드는이 리소스에 대한 액세스 권한도 필요합니다. 최악의 경우 : 로우 프리 퍼 스레드가 잠금을 보유하고 높은 우선 스레드가 시작되어 잠금에 액세스합니다. 이 경우, 로우 프리 오 (low prio) 쓰레드는 "비활성"으로 스케쥴되고 하이 프리 오 쓰레드는 "활성"으로 스케쥴된다. 문제점 : 로우 프리 피어 스레드가 여전히 잠금을 유지하기 때문에 하이 프리오 스레드가 차단되었습니다.

이 문제는 독자가 읽을 때 분명하지만 복잡한 코드에서는 항상 명확하지는 않습니다 ... 잠금 범위를 줄이면 이러한 상황을 피할 수 있습니다.

가능한 경우 비동기로 전환 할 수도 있지만 교착 상태 시나리오가있을 수 있습니다 (예 : post 참조).

0

C# lock 문은 여러 스레드에서 중요한 섹션에 대한 액세스를 제한하는 데 사용됩니다. 이 명령문은 잠금 해제까지 단일 스레드 실행에 대해 상호 배타적 인 액세스를 보장합니다. 예를 들어, 쓰기 조작이 진행되는 동안 파일 쓰기 조건이 모든 액세스를 차단하도록 설정된 경우, 파일 쓰기 코드 주위에 lock 문을 랩핑하면 조건을 만족시킵니다.

이제 실행 시간이 길어지면 다른 스레드가 잠금 대기열에 있습니다. 응답 시간이 느려지거나 성능이 저하되거나 시간 초과로 인해 통화 실패가 발생할 수 있습니다. 예 -

private Object lockObj = new Object(); 

public void FileOperation(decimal amount) 
{ 
    //Subsequent thread calls will queue up 
    lock (lockObj) 
    {   
     using (System.IO.StreamWriter file = 
       new System.IO.StreamWriter(@"C:\Users\Public\TestFolder\WriteLines2.txt", true)) 
      { 
       //Long running operation 
       for(var i = 1; i < 1000000; i++) 
       { 
        file.WriteLine("Fourth line"); 
       } 
      } 
    } 
} 

Original Post