[c#] 잠긴 객체는 내부에서 예외가 발생하면 잠긴 상태로 유지됩니까?

ac # 스레딩 앱에서 개체를 잠 그려면 대기열을 말하고 예외가 발생하면 개체가 잠긴 상태로 유지됩니까? 다음은 의사 코드입니다.

int ii;
lock(MyQueue)
{
   MyClass LclClass = (MyClass)MyQueue.Dequeue();
   try
   {
      ii = int.parse(LclClass.SomeString);
   }
   catch
   {
     MessageBox.Show("Error parsing string");
   }
}

내가 알기로는 catch 후 코드가 실행되지 않지만 잠금이 해제되는지 궁금합니다.



답변

먼저; TryParse를 고려해 보셨습니까?

in li;
if(int.TryParse(LclClass.SomeString, out li)) {
    // li is now assigned
} else {
    // input string is dodgy
}

잠금은 두 가지 이유로 해제됩니다. 첫째, lock본질적으로 다음과 같습니다.

Monitor.Enter(lockObj);
try {
  // ...
} finally {
    Monitor.Exit(lockObj);
}

둘째; 내부 예외를 포착하고 다시 던지지 lock않으므로 실제로 예외를 보지 않습니다. 물론 MessageBox 기간 동안 잠금을 유지하고 있으며 이는 문제가 될 수 있습니다.

따라서 가장 치명적인 치명적인 복구 불가능한 예외를 제외하고 모두 릴리스 될 것입니다.


답변

나는 예외에 대한 잠금해제하는 것이 엄청나게 위험한 일이라고이 오래된 질문에 대한 답변에서 아무도 언급하지 않았습니다 . 예, C #의 잠금 문에는 “최종”의미가 있습니다. 제어가 잠금을 정상적으로 또는 비정상적으로 종료하면 잠금이 해제됩니다. 당신은 모두 좋은 일인 것처럼 이야기하고 있지만 나쁜 일입니다! 처리되지 않은 예외를 발생시키는 잠긴 영역이있는 경우해야 할 올바른 일은 잠금을 해제 하고 계속 진행하는 것이 아니라 더 많은 사용자 데이터를 파괴하기 직전에 병든 프로세스종료하는 입니다.

이런 식으로보세요. 문에 자물쇠가 달린 화장실과 밖에서 기다리는 사람들이 있다고 가정 해 보겠습니다. 화장실에서 폭탄이 터져서 그 안에있는 사람이 죽습니다. 귀하의 질문은 “그런 상황에서 다음 사람이 화장실에 들어갈 수 있도록 자물쇠가 자동으로 해제됩니까?”입니다. 네, 그럴 것입니다. 그것은 좋은 일이 아닙니다. 폭탄이 터져서 누군가를 죽였습니다! 배관이 파괴되었을 수 있고 집은 더 이상 구조적으로 건전하지 않으며 거기에 또 다른 폭탄이있을 수 있습니다 . 해야 할 옳은 일은 가능한 한 빨리 모든 사람을 내보내고 집 전체를 철거하는 것입니다.

내 말은, 생각해보십시오. 다른 스레드에서 변경되지 않고 데이터 구조에서 읽기 위해 코드 영역을 잠그고 해당 데이터 구조에서 예외가 발생한 경우 데이터 구조가 그 때문일 가능성이 높습니다. 손상되었습니다 . 이제 사용자 데이터가 엉망입니다. 손상된 데이터 를 저장하기 때문에이 시점에서 사용자 데이터를 저장 하지 않으려 고합니다 . 프로세스를 종료하십시오.

다른 스레드가 동시에 상태를 읽지 않고 변형을 수행하기 위해 코드 영역을 잠그고 변형이 발생 하면 이전에 데이터가 손상되지 않았 으면 이제 . 잠금이 보호 해야하는 시나리오는 정확히 무엇입니까 ? 이제 해당 상태를 읽기 위해 대기중인 코드는 즉시 손상된 상태에 액세스 할 수 있으며 아마도 자체적으로 충돌 할 것입니다. 다시 말하지만 올바른 방법은 프로세스를 종료하는 것입니다.

어떻게 슬라이스하든 잠금 내부의 예외는 나쁜 소식 입니다. 질문해야 할 올바른 질문은 “예외가 발생하면 내 잠금이 정리됩니까?”가 아닙니다. 질문해야 할 올바른 질문은 “잠금 내부에 예외가 없는지 어떻게 확인합니까? 그리고 만약 있다면, 변이가 이전의 양호한 상태로 롤백되도록 프로그램을 어떻게 구성합니까?”입니다.


답변

예, 제대로 해제됩니다. lock역할 try/ finally의와 Monitor.Exit(myLock)최종적으로, 그래서 아무리 당신이 그것을 종료하는 방법 공개되지 않습니다이다. 참고로, catch(... e) {throw e;}스택 추적이 손상되므로 피하는 것이 가장 좋습니다 e. 전혀 잡지 않는 것이 낫 습니다 . 또는 다른 방법으로 는 다시 던지는 throw;것보다 사용하십시오 throw e;.

정말로 알고 싶다면 C # 4 / .NET 4의 잠금은 다음과 같습니다.

{
    bool haveLock = false;
    try {
       Monitor.Enter(myLock, ref haveLock);
    } finally {
       if(haveLock) Monitor.Exit(myLock);
    }
}


답변

“잠금 문은 Monitor.Enter를 호출 한 다음 try… finally 블록으로 컴파일됩니다. finally 블록에서 Monitor.Exit가 호출됩니다.

x86 및 x64 모두에 대한 JIT 코드 생성은 Monitor.Enter 호출과 바로 뒤에 오는 try 블록 사이에 스레드 중단이 발생하지 않도록합니다. “

출처 :
이 사이트


답변

잠금이 제대로 해제됩니다. A lock는 다음과 같이 작동합니다.

try {
    Monitor.Enter(myLock);
    // ...
} finally {
    Monitor.Exit(myLock);
}

그리고 finally블록을 떠나는 방법에 관계없이 블록은 실행이 보장됩니다 try.


답변

Marc의 탁월한 답변에 약간을 추가하십시오.

이와 같은 상황은 lock키워드 가 존재하는 바로 그 이유입니다 . 개발자가 잠금이 해제되었는지 확인하는 데 도움이됩니다.finally 블록 .

당신이 사용을 강제하는 경우 Monitor.Enter/ Exit시간 제한을 지원하기 위해 예를 들어, 당신에게 전화를 걸 수 있는지 확인해야합니다 Monitor.Exit에서 finally예외의 경우에 잠금 적절한 릴리스를 확인하기 위해 블록.


답변