C #의 다양한 스레딩 동기화 옵션의 차이점은 무엇입니까?


답변:


135

좋은 질문입니다. 어쩌면 내가 틀렸을 수도있다 나를 읽어 주셔서 감사합니다 :)

잠금 (obj)

  • (intra-object?) 스레드 동기화를위한 CLR 구문입니다. 하나의 스레드 만 객체 잠금을 소유하고 잠금 된 코드 블록을 입력 할 수 있도록합니다. 다른 스레드는 현재 소유자가 코드 블록을 종료하여 잠금을 해제 할 때까지 기다려야합니다. 또한 클래스의 개인 멤버 객체를 잠그는 것이 좋습니다.

모니터

  • lock (obj)은 모니터를 사용하여 내부적으로 구현됩니다. 정리 절차를 잊어 버리는 것처럼 방해하지 않기 때문에 lock (obj)을 선호해야합니다. 당신이 원한다면 그것은 모니터 구성을 '바보 방지'합니다.
    모니터는 .NET Framework 용으로 특별히 설계되어 리소스를 더 잘 사용하기 때문에 일반적으로 뮤텍스보다 모니터를 사용하는 것이 좋습니다.

잠금 또는 모니터를 사용하면 스레드에 민감한 코드 블록의 동시 실행을 방지하는 데 유용하지만 이러한 구성을 통해 한 스레드가 다른 이벤트와 통신 할 수는 없습니다. 이를 위해서는 스레드를 활성화하고 일시 중단하는 데 사용할 수있는 신호 및 신호 없음 상태 중 하나의 객체 인 동기화 이벤트가 필요 합니다. Mutex, Semaphores는 OS 수준의 개념입니다. 예를 들어 명명 된 mutex를 사용하면 여러 (관리되는) exe간에 동기화 할 수 있습니다 (시스템에서 하나의 응용 프로그램 인스턴스 만 실행되도록 보장).

뮤텍스 :

  • 그러나 모니터와 달리 뮤텍스를 사용하여 프로세스 간 스레드를 동기화 할 수 있습니다. 프로세스 간 동기화에 사용되는 뮤텍스는 다른 응용 프로그램에서 사용되기 때문에 명명 된 뮤텍스 라고하며 , 따라서 전역 또는 정적 변수를 통해 공유 할 수 없습니다. 두 애플리케이션 모두 동일한 뮤텍스 객체에 액세스 할 수 있도록 이름을 지정해야합니다. 반대로 Mutex 클래스는 Win32 구문의 래퍼입니다. 모니터보다 강력하지만 뮤텍스에는 Monitor 클래스에 필요한 것보다 계산 비용이 많이 드는 interop 전환이 필요합니다.

세마포어 (뇌가 아프다).

  • Semaphore 클래스를 사용하여 자원 풀에 대한 액세스를 제어하십시오. 스레드는 WaitHandle 클래스에서 상속 된 WaitOne 메소드를 호출하여 세마포어에 들어가고 Release 메소드를 호출하여 세마포어를 해제합니다. 스레드가 세마포어에 들어갈 때마다 세마포어의 개수가 감소하고 스레드가 세마포어를 놓을 때 증가합니다. 개수가 0이면 다른 스레드가 세마포어를 해제 할 때까지 후속 요청이 차단됩니다. 모든 스레드가 세마포어를 해제하면 카운트는 세마포어가 작성 될 때 지정된 최대 값에 있습니다. 스레드는 세마포어를 여러 번 입력 할 수 있습니다. Semaphore 클래스는 WaitOne 또는 Release .. 프로그래머에게 스레드를 강제로 해제하지 않아야합니다. 세마포어는 로컬 세마포어와 이름이 있습니다.시스템 세마포어. 이름을 허용하는 생성자를 사용하여 Semaphore 오브젝트를 작성하면 해당 이름의 운영 체제 세마포어와 연관됩니다. 명명 된 시스템 세마포어는 운영 체제 전체에서 볼 수 있으며 프로세스 활동을 동기화하는 데 사용할 수 있습니다. 로컬 세마포어는 프로세스 내에서만 존재합니다. 로컬 세마포어 객체에 대한 참조가있는 프로세스의 모든 스레드에서 사용할 수 있습니다. 각 세마포어 객체는 별도의 로컬 세마포어입니다.

읽을 페이지-스레드 동기화 (C #)


18
Monitor의사 소통을 허용하지 않는다고 주장합니다 . 할 수 있습니다 여전히 Pulse등등에Monitor
마크 Gravell

3
- 세마포어에 대한 대체 설명 확인 stackoverflow.com/a/40473/968003을 . 나이트 클럽에서 세마포어를 경비원으로 생각하십시오. 클럽에는 한 번에 허용 된 인원이 있습니다. 클럽이 가득 차면 아무도 입장 할 수 없지만 한 사람이 떠나 자마자 다른 사람이 입장 할 수 있습니다.
Alex Klaus

31

"다른 .Net 동기화 클래스 사용"을 다시 참조하십시오.

CCR / TPL ( Parallel Extensions CTP) 에는 더 많은 (오버 헤드가 적은) 잠금 구성이 있지만 IIRC는 .NET 4.0에서 사용할 수 있습니다.


따라서 간단한 신호 통신 (예 : 비동기 op 완료)을 원한다면 Monitor.Pulse? 또는 SemaphoreSlim 또는 TaskCompletionSource를 사용합니까?
Vivek

비동기 작업에는 TaskCompletionSource를 사용하십시오. 기본적으로 스레드에 대한 생각을 멈추고 작업 (작업 단위)에 대한 생각을 시작하십시오. 스레드는 구현 세부 사항이며 관련이 없습니다. TCS를 반환하면 결과, 오류 또는 처리 취소를 반환 할 수 있으며 다른 비동기 작업 (예 : async await 또는 ContinueWith)과 쉽게 구성 할 수 있습니다.
Simon Gillbee

14

ECMA에 명시된 바와 같이 Reflected 메소드에서 볼 수 있듯이 lock 문은 기본적으로

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
   
}
finally {
   System.Threading.Monitor.Exit(obj);
}

앞에서 언급 한 예에서 모니터가 객체를 잠글 수 있음을 알 수 있습니다.

Mutexe 는 문자열 식별자를 잠글 있으므로 프로세스 간 동기화가 필요할 때 유용합니다 . 다른 프로세스에서 동일한 문자열 식별자를 사용하여 잠금을 획득 할 수 있습니다.

세마포어는 스테로이드의 뮤텍스와 유사하며 최대 동시 액세스 수를 제공하여 동시 액세스를 허용합니다. 한도에 도달하면 호출자 중 하나가 세마포어를 해제 할 때까지 세마포어가 리소스에 대한 추가 액세스를 차단하기 시작합니다.


5
이 구문 설탕은 C # 4에서 약간 변경되었습니다. blogs.msdn.com/ericlippert/archive/2009/03/06/…에서
Peter Gfader

14

DotGNU에서 스레딩에 대한 클래스 및 CLR 지원을했는데 몇 가지 생각이 있습니다 ...

교차 프로세스 잠금이 필요하지 않으면 항상 Mutex & Semaphores를 사용하지 않아야합니다. .NET의 이러한 클래스는 Win32 Mutex 및 Semaphores를 감싸는 래퍼이며 다소 무겁습니다 (커널에 컨텍스트 전환이 필요하므로 비용이 많이 듭니다. 특히 잠금이 경합이 아닌 경우).

다른 사람들이 언급했듯이 C # lock 문은 Monitor.Enter 및 Monitor.Exit (시도 / 최종 내에 존재)에 대한 컴파일러 마술입니다.

모니터에는 Mutex가 Monitor.Pulse / Monitor.Wait 메소드를 통해 가지고 있지 않은 단순하지만 강력한 신호 / 대기 메커니즘이 있습니다. Win32에 해당하는 것은 CreateEvent를 통한 이벤트 객체이며 실제로 .NET에는 WaitHandles로도 존재합니다. Pulse / Wait 모델은 Unix의 pthread_signal 및 pthread_wait와 유사하지만 경쟁이없는 경우 완전히 사용자 모드 작업이 될 수 있으므로 더 빠릅니다.

Monitor.Pulse / Wait는 사용이 간편합니다. 하나의 스레드에서 객체를 잠그고 플래그 / 상태 / 속성을 확인하고 예상 한 것이 아닌 경우 Monitor.Wait를 호출하여 잠금을 해제하고 펄스가 전송 될 때까지 기다립니다. 대기가 돌아 오면 루프백하고 플래그 / 상태 / 속성을 다시 확인합니다. 다른 스레드에서는 플래그 / 상태 / 속성을 변경할 때마다 객체를 잠근 다음 PulseAll을 호출하여 수신 스레드를 깨 웁니다.

종종 우리는 클래스가 스레드로부터 안전하기를 원하므로 코드에 잠금을 설정합니다. 그러나 종종 클래스가 하나의 스레드에서만 사용되는 경우가 종종 있습니다. 이는 잠금이 코드를 불필요하게 느리게하는 것을 의미합니다. 이것은 CLR의 영리한 최적화가 성능 향상에 도움이되는 곳입니다.

Microsoft의 잠금 구현은 확실하지 않지만 DotGNU 및 Mono에서는 잠금 상태 플래그가 모든 개체의 헤더에 저장됩니다. .NET (및 Java)의 모든 객체는 잠금이 될 수 있으므로 모든 객체는 헤더에서이를 지원해야합니다. DotGNU 구현에는 잠금으로 사용되는 모든 객체에 대해 전역 해시 테이블을 사용할 수있는 플래그가 있습니다. 이는 모든 객체에 대해 4 바이트 오버 헤드를 제거 할 수 있다는 이점이 있습니다. 이것은 메모리 (특히 스레드가 많지 않은 임베디드 시스템)에는 좋지 않지만 성능에 영향을 미칩니다.

Mono와 DotGNU는 효과적으로 뮤텍스를 사용하여 잠금 / 대기를 수행하지만 스핀 록 스타일 비교 및 교환 작업을 사용하여 실제로 필요한 경우가 아니라면 실제로 하드 잠금을 수행 할 필요가 없습니다.

여기에서 모니터를 구현하는 방법의 예를 볼 수 있습니다.

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup


9

문자열 ID로 식별 한 공유 Mutex에 대한 추가 경고는 기본적으로 "Local \"mutex로 설정되며 터미널 서버 환경에서 세션간에 공유되지 않는다는 것입니다.

공유 시스템 자원에 대한 액세스가 올바르게 제어되도록 문자열 식별자 앞에 "Global \"을 붙입니다. 나는 이것을 깨닫기 전에 SYSTEM 계정으로 실행되는 서비스와 통신을 동기화하는 데 많은 문제가 발생했습니다.


5

가능한 경우 "lock ()", "Mutex"및 "Monitor"를 피하려고합니다.

.NET 4의 새로운 네임 스페이스 System.Collections.Concurrent을 확인
그것은 멋진 스레드 안전 컬렉션 클래스를 가지고

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

동시 사전! 더 이상 수동 잠금 기능이 없습니다!


2
잠그지 말고 모니터를 사용 하시겠습니까? 왜?
mafu

@mafutrct 동기화를 직접 관리해야하기 때문입니다.
Peter Gfader

아, 이제 알겠다. 언급 한 세 가지 아이디어를 모두 피해야한다. 모니터를 사용하지만 잠금 / 음소거를 사용하지 않는 것처럼 들렸습니다.
mafu

System.Collections.Concurrent를 사용하지 마십시오. 경쟁 조건의 주요 원천이며 발신자 스레드도 차단합니다.
Alexander Danilov

-2

대부분의 경우 잠금 (= 모니터) 또는 뮤텍스 / 세마포어를 사용 하지 않아야 합니다. 그들은 모두 현재 스레드를 차단합니다.

그리고 클래스를 사용해서는 안됩니다. System.Collections.Concurrent 클래스는 여러 컬렉션 간의 트랜잭션을 지원하지 않고 현재 스레드를 차단하기 때문에 경쟁 조건의 주요 원인입니다.

놀랍게도 .NET에는 효과적인 동기화 메커니즘이 없습니다.

테스트를 통해 스레드 풀을 사용하는 동기화 도구를 차단하지 않고 매우 가벼운 C # 에서 GCD ( 세계)의 직렬 대기열 을 구현했습니다 Objc/Swift.

데이터베이스 액세스 (hello sqlite)에서 비즈니스 로직에 이르기까지 대부분의 경우 동기화하는 가장 좋은 방법입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.