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


85

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

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

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


1
마지막으로 (업데이트 참조)-대기열에서 빼는 동안 만 잠금을 유지해야합니다 . 잠금 외부 에서 처리를 수행합니다 .
Marc Gravell

1
예외가 처리 되었기 때문에 catch 후 코드가 실행 됨
cjk

감사합니다.이 질문을 놓쳤을 것입니다.이 질문을 삭제해야합니까?
Vort3x

4
샘플 코드가이 질문에 좋지 않은 것 같지만 질문은 꽤 유효합니다.
SalvadorGomez

C #을 디자이너 - 잠금 및 예외
Jivan

답변:


88

먼저; 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 기간 동안 잠금을 유지하고 있으며 이는 문제가 될 수 있습니다.

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


14
나는 tryparse를 알고 있지만 내 질문과 실제로 관련이 없습니다. 이것은 질문을 설명하는 간단한 코드였으며 구문 분석에 관한 진정한 관심사는 아닙니다. 와 구문 분석 교체하십시오 어떤 캐치를 강제로 편안하게하는 코드를.
Khadaji

13
new Exception ( "설명을 위해"); ;-p
Marc Gravell

1
TheadAbortException사이에 a 가 발생 하는 경우 제외 : blogs.msdn.com/ericlippert/archive/2009/03/06/…Monitor.Entertry
Igal Tabachnik

2
하천을 건너는 것과 같은 "치명적인 치명적인 복구 불가능한 예외".
필 쿠퍼

98

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

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

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

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

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


18
이 문제는 IMO 잠금과 매우 직교합니다. 예상되는 예외가 발생하면 잠금을 포함하여 모든 것을 정리하려고합니다. 예기치 않은 예외가 발생하면 잠금 유무에 관계없이 문제가있는 것입니다.
CodesInChaos 2012

10
위에서 설명한 상황은 일반주의라고 생각합니다. 때때로 예외는 치명적인 사건을 설명합니다. 때로는 그렇지 않습니다. 각각은 코드에서 서로 다르게 사용합니다. 예외가 예외적이지만 치명적이지 않은 이벤트의 신호라는 것은 완벽하게 타당합니다. 예외 = 치명적이라고 가정하면 프로세스 종료 케이스가 너무 구체적이라고 가정합니다. 재앙적인 사건 일 수 있다는 사실은 질문의 타당성을 제거하지 않습니다. 동일한 사고 방식이 예외를 처리하지 못하게 할 수 있으며,이 경우 프로세스가 종료됩니다 ...
Gerasimos R

1
@GerasimosR : 그렇습니다. 강조 할 두 가지 요점. 첫째, 예외는 무해한 것으로 결정될 때까지 치명적인 것으로 간주되어야합니다. 둘째, 잠긴 영역에서 무해한 예외가 발생하면 잠긴 영역이 잘못 설계되었을 수 있습니다. 그것은 아마도 자물쇠 안에서 너무 많은 일을하고있을 것입니다.
Eric Lippert

1
모두 존경심을 표합니다.하지만 당신은 Lippert 씨가 finally 문을 사용하는 잠금 블록의 C # 디자인이 나쁜 것이라고 말하는 것처럼 들립니다. 대신 잠금 내에서 포착되지 않은 잠금 문에 예외가있는 모든 앱을 완전히 충돌시켜야합니다. 아마도 그것은 당신이 말하는 것이 아닐 수도 있지만, 만약 그렇다면 팀이 당신의 (순수 주의적 이유로 극단 주의자!) 경로가 아닌 그들이 한 경로를 갔다는 것이 정말 기쁩니다. 모두 존경합니다.
Nicholas Petersen

2
구성 불가가 의미하는 바가 명확하지 않은 경우 : 두 개의 목록, s 및 d, 잠금 s, 잠금 d, s에서 항목 제거, d에 항목 추가, 잠금 해제를 취하는 "전송"메소드가 있다고 가정하십시오. d, s를 잠금 해제합니다. 이 방법은 다른 누군가가 Y에서 X로 전송을 시도하는 것과 동시에 목록 X에서 목록 Y로 전송을 시도하지 않는 경우 에만 정확 합니다 . 잠금은 전역 상태의 안전하지 않은 변형 이기 때문에 전송 방법의 정확성으로 인해 더 큰 문제에 대한 올바른 솔루션을 구축 할 수 없습니다 . 안전하게 "전송"하려면 프로그램의 모든 잠금 에 대해 알아야합니다 .
Eric Lippert

43

예, 제대로 해제됩니다. 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);
    }
} 

14

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

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

출처 : 이 사이트


1
이것이 사실이 아닌 경우가 하나 이상 있습니다. 4 이전의 .net 버전에서 디버그 모드에서 스레드 중단이 발생합니다. 그 이유는 C # 컴파일러가 Monitor.Enter와 사이에 NOP를 삽입 try하여 "즉시 따르기"조건이 JIT가 위반되었습니다.
CodesInChaos 2012

6

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

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

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


사실, 어떤 코드 (당신은 예를 들어, 전원 케이블을 당겨 수) 실행 "을 보장하지"하고, 즉 확실히하지 4.0 같은 잠금 보이는 것 - 여기에서 볼
마크 Gravell

1
@MarcGravell : 정확한 두 점에 대해 두 개의 각주를 넣는 것에 대해 생각했습니다. 그리고 나는 그것을 :)별로 중요하지 않을 생각
Ry-

1
@MarcGravel : 프로그래머가 제어 할 수있는 것이 아니기 때문에 모든 사람들이 항상 '플러그 더 플러그'상황에 대해 이야기하지 않는다고 가정한다고 생각합니다. :)
Vort3x

5

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

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

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

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