모니터 대 잠금


89

C #에서 스레드 안전성을 위해 Monitor클래스 또는 lock키워드 를 사용하는 것이 적절한 경우는 언제 입니까?

편집 : 지금까지의 답변 lock에서 일련의 Monitor클래스 호출에 대한 짧은 손으로 보입니다 . 잠금 호출의 약어는 정확히 무엇입니까? 또는 더 명시 적으로

class LockVsMonitor
{
    private readonly object LockObject = new object();
    public void DoThreadSafeSomethingWithLock(Action action)
    {
        lock (LockObject)
        {
            action.Invoke();
        }
    }
    public void DoThreadSafeSomethingWithMonitor(Action action)
    {
        // What goes here ?
    }
}

최신 정보

도움을 주셔서 감사합니다. 귀하가 제공 한 정보 중 일부에 대한 후속 질문으로 다른 질문을 게시했습니다. 이 분야에 정통한 것 같기 때문에 다음 링크를 게시했습니다. 잠긴 예외를 잠그고 관리하는이 솔루션의 문제점은 무엇입니까?

답변:


89

Eric Lippert는 그의 블로그에서 이에 대해 이야기합니다. 잠금과 예외는 섞이지 않습니다.

해당 코드는 C # 4.0과 이전 버전간에 다릅니다.


C # 4.0에서는 다음과 같습니다.

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    { body }
}
finally
{
    if (lockWasTaken) Monitor.Exit(temp);
}

Monitor.Enter잠금을 취할 때 플래그 를 원자 적으로 설정하는 것에 의존합니다 .


그리고 이전에는 다음과 같습니다.

var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}

이것은와 사이에 예외가 발생하지 않는 것에 의존 Monitor.Enter합니다 try. 나는 컴파일러가 그들 사이에 NOP를 삽입하고 그 사이에 스레드 중단을 가능하게했기 때문에 디버그 코드 에서이 조건이 위반되었다고 생각합니다.


내가 말했듯이 첫 번째 예제는 C # 4이고 다른 하나는 이전 버전에서 사용하는 것입니다.
CodesInChaos

참고로 CLR을 통한 C #에서는 잠금 키워드에 대한주의 사항을 언급합니다. 잠금을 해제하기 전에 손상된 상태 (해당되는 경우)를 복원하기 위해 작업을 수행 할 수 있습니다. lock 키워드를 사용하면 catch 블록에 항목을 넣을 수 없으므로 사소한 루틴이 아닌 경우 긴 버전의 try-catch-finally 작성을 고려해야합니다.
kizzx2

5
공유 상태를 복원하는 IMO는 잠금 / 다중 스레딩과 직교합니다. 따라서 lock블록 내부에서 try-catch / finally로 수행해야합니다 .
CodesInChaos

2
@ kizzx2 : 이러한 패턴은 독자-작성기 잠금에 특히 좋습니다. 판독기 잠금을 보유하는 코드 내에서 예외가 발생하면 보호 된 리소스가 손상 될 것이라고 예상 할 이유가 없으므로 무효화 할 이유가 없습니다. 작성기 잠금 내에서 예외가 발생하고 예외 처리 코드가 보호 대상 개체의 상태가 복구되었음을 명시 적으로 나타내지 않는 경우 개체가 손상되었을 수 있으므로 무효화해야 함을 의미합니다. IMHO, 예기치 않은 예외가 프로그램을 중단해서는 안되지만 손상 될 수있는 모든 것을 무효화해야합니다.
supercat

2
@ArsenZahray Pulse간단한 잠금이 필요하지 않습니다 . 일부 고급 다중 스레딩 시나리오에서 중요합니다. Pulse직접 사용한 적이 없습니다 .
CodesInChaos

43

lock단지에 대한 바로 가기입니다 Monitor.Entertry+ finallyMonitor.Exit. 충분할 때마다 lock 문을 사용하십시오. TryEnter와 같은 것이 필요하면 Monitor를 사용해야합니다.


23

잠금 문은 다음과 같습니다.

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

그러나 Monitor는 복잡한 멀티 스레딩 상황에서 종종 유용한 Wait ()Pulse ()를 사용할 수도 있습니다.

최신 정보

그러나 C # 4에서는 다르게 구현됩니다.

bool lockWasTaken = false;
var temp = obj;
try 
{
     Monitor.Enter(temp, ref lockWasTaken); 
     //your code
}
finally 
{ 
     if (lockWasTaken) 
             Monitor.Exit(temp); 
} 

코멘트와 링크 를 위해 CodeInChaos에 고맙습니다


C # 4에서는 잠금 문이 다르게 구현됩니다. blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
CodesInChaos

14

Monitor더 유연합니다. 내가 가장 좋아하는 모니터 사용 사례는 차례 를 기다리지 않고 그냥 건너 뛰는 것입니다 .

//already executing? forget it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}

6

다른 사람들이 말했듯 lock이 "동등한"

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

그러나 호기심으로 lock인해 전달한 첫 번째 참조를 보존하고 변경하면 던지지 않습니다. 잠긴 개체를 변경하지 않는 것이 좋으며 그렇게하고 싶지 않다는 것을 알고 있습니다.

그러나 다시 한 번, 과학적으로 이것은 잘 작동합니다.

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        lock (lockObject)
        {
            lockObject += "x";
        }
    }));
Task.WaitAll(tasks.ToArray());

... 그리고 이것은 :

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        Monitor.Enter(lockObject);
        try
        {
            lockObject += "x";
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }));
Task.WaitAll(tasks.ToArray());

오류:

70783sTUDIES.exe에서 'System.Threading.SynchronizationLockException'유형의 예외가 발생했지만 사용자 코드에서 처리되지 않았습니다.

추가 정보 : 개체 동기화 메서드가 동기화되지 않은 코드 블록에서 호출되었습니다.

이것은 불변 이기 때문에 변경된 Monitor.Exit(lockObject);것에 lockObject대해 작동 하기 때문 입니다 strings. 그러면 동기화되지 않은 코드 블록에서 호출합니다. 그러나 어쨌든. 이것은 단지 재미있는 사실입니다.


"이것은 Monitor.Exit (lockObject);가 lockObject에서 작동하기 때문입니다." 그런 다음 잠금은 개체와 함께 작동하지 않습니까? 잠금은 어떻게 작동합니까?
Yugo Amaryl

@YugoAmaryl, 나는 lock 문이 먼저 전달 된 참조를 염두에두고 다음과 같이 변경된 참조를 사용하는 대신 사용하기 때문이라고 생각합니다.object temp = lockObject; Monitor.Enter(temp); <...locked code...> Monitor.Exit(temp);
Zhuravlev A.

3

둘 다 같은 것입니다. lock은 c sharp 키워드이며 Monitor 클래스를 사용합니다.

http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx


3
msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx를 보십시오. "실제로 lock 키워드는 Monitor 클래스로 구현됩니다. 예를 들어"
RobertoBr

1
잠금의 기본 구현은 Monitor를 사용하지만 동일한 것은 아닙니다. 잠금을 위해 존재하지 않는 모니터에서 제공하는 메소드와 별도의 코드 블록에서 잠금 및 잠금 해제 할 수있는 방법을 고려하십시오.
eran otzap 2013-06-03

3

모니터의 잠금 및 기본 동작 (입력 + 종료)은 다소 동일하지만 모니터에는 더 많은 동기화 가능성을 허용하는 더 많은 옵션이 있습니다.

자물쇠는 지름길이며 기본적인 사용법을위한 옵션입니다.

더 많은 제어가 필요한 경우 모니터가 더 나은 옵션입니다. 고급 용도 (예 : 장벽, 세마포어 등)를 위해 Wait, TryEnter 및 Pulse를 사용할 수 있습니다.


1

Lock Lock 키워드는 한 스레드가 한 번에 코드 조각을 실행하도록합니다.

잠금 (lockObject)

        {
        //   Body
        }

lock 키워드는 주어진 객체에 대한 상호 배제 잠금을 획득하고 명령문을 실행 한 다음 잠금을 해제하여 명령문 블록을 중요 섹션으로 표시합니다.

다른 스레드가 잠긴 코드를 입력하려고하면 개체가 해제 될 때까지 대기하고 차단합니다.

Monitor Monitor는 정적 클래스이며 System.Threading 네임 스페이스에 속합니다.

주어진 시점에서 단 하나의 스레드 만 중요 섹션에 들어갈 수 있도록 개체에 대한 배타적 잠금을 제공합니다.

C #에서 모니터와 잠금의 차이점

잠금은 Monitor.Enter의 바로 가기입니다. 잠금 핸들은 내부적으로 try and finally 차단합니다. Lock = Monitor + try finally

당신이 더 많은 컨트롤을 사용하여 고급 멀티 스레딩 솔루션을 구현하려면 TryEnter() Wait(), Pulse()PulseAll()방법을 다음 모니터 클래스는 옵션입니다.

C # Monitor.wait(): 스레드는 다른 스레드가 알릴 때까지 기다립니다.

Monitor.pulse(): 스레드가 다른 스레드에 알립니다.

Monitor.pulseAll(): 스레드는 프로세스 내의 다른 모든 스레드에 알립니다.


0

위의 모든 설명 외에도 lock은 C # 문인 반면 Monitor는 System.Threading 네임 스페이스에있는 .NET 클래스입니다.

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