[C#] 리턴 문이 잠금 내부 또는 외부에 있어야합니까?

방금 내 코드의 어느 곳에서 잠금 내부와 외부에 return 문이 있음을 깨달았습니다. 어느 것이 최고입니까?

1)

void example()
{
    lock (mutex)
    {
    //...
    }
    return myData;
}

2)

void example()
{
    lock (mutex)
    {
    //...
    return myData;
    }

}

어느 것을 사용해야합니까?



답변

기본적으로 코드가 단순 해집니다. 단일 종료 지점은 이상적이지만, 코드를 달성하기 위해 코드를 구부리지 않을 것입니다 … 그리고 대안이 로컬 변수 (잠금 외부)를 선언하고 초기화하면 (잠금 내부) 그런 다음 (잠금 외부에) 반환하면 잠금 내부의 간단한 “반환 foo”가 훨씬 간단하다고 말하고 싶습니다.

IL의 차이점을 보여주기 위해 다음과 같이 코드를 작성하십시오.

static class Program
{
    static void Main() { }

    static readonly object sync = new object();

    static int GetValue() { return 5; }

    static int ReturnInside()
    {
        lock (sync)
        {
            return GetValue();
        }
    }

    static int ReturnOutside()
    {
        int val;
        lock (sync)
        {
            val = GetValue();
        }
        return val;
    }
}

(나는 그것이 ReturnInside더 간단하고 깨끗한 C # 비트 라고 행복하게 주장 할 것 입니다)

그리고 IL (릴리스 모드 등)을보십시오.

.method private hidebysig static int32 ReturnInside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 CS$1$0000,
        [1] object CS$2$0001)
    L_0000: ldsfld object Program::sync
    L_0005: dup
    L_0006: stloc.1
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0
    L_0012: leave.s L_001b
    L_0014: ldloc.1
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally
    L_001b: ldloc.0
    L_001c: ret
    .try L_000c to L_0014 finally handler L_0014 to L_001b
}

method private hidebysig static int32 ReturnOutside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 val,
        [1] object CS$2$0000)
    L_0000: ldsfld object Program::sync
    L_0005: dup
    L_0006: stloc.1
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0
    L_0012: leave.s L_001b
    L_0014: ldloc.1
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally
    L_001b: ldloc.0
    L_001c: ret
    .try L_000c to L_0014 finally handler L_0014 to L_001b
}

IL 수준에서 그것들은 (이름을 지어 주거나) 동일합니다 (나는 무언가를 배웠습니다 ;-p). 따라서 유일하게 현명한 비교는 지역 코딩 스타일의 (매우 주관적인) 법칙입니다 … 나는 ReturnInside단순성을 선호 하지만 어느 쪽도 흥분하지 않을 것입니다.


답변

아무런 차이가 없습니다. 둘 다 컴파일러에 의해 동일한 것으로 번역됩니다.

명확히하기 위해 다음 의미를 가진 것으로 효과적으로 변환됩니다.

T myData;
Monitor.Enter(mutex)
try
{
    myData= // something
}
finally
{
    Monitor.Exit(mutex);
}

return myData;


답변

나는 확실히 반품을 자물쇠 안에 넣을 것이다. 그렇지 않으면 다른 스레드가 잠금을 입력하고 return 문 전에 변수를 수정하여 원래 호출자가 예상과 다른 값을 받도록 할 수 있습니다.


답변

때에 따라 다르지,

나는 곡물에 반대 할 것입니다. 나는 일반적으로 자물쇠 안쪽으로 돌아갑니다.

일반적으로 변수 mydata는 지역 변수입니다. 로컬 변수를 초기화하는 동안 선언하는 것을 좋아합니다. 내 자물쇠 밖에서 반환 값을 초기화 할 데이터가 거의 없습니다.

따라서 비교에는 실제로 결함이 있습니다. 이상적으로 두 옵션의 차이점은 당신이 작성한 것과 같을 것입니다. 사례 1에 끄덕이는 것은 실제로 조금 더 추합니다.

void example() {
    int myData;
    lock (foo) {
        myData = ...;
    }
    return myData
}

vs.

void example() {
    lock (foo) {
        return ...;
    }
}

특히 짧은 스 니펫의 경우 사례 2가 읽기 쉽고 조이기 어렵다는 것을 알았습니다.


답변

외부 잠금이 더 좋아 보이지만 코드를 다음과 같이 변경하면 조심하십시오.

return f(...)

잠금을 유지 한 상태에서 f ()를 호출해야하는 경우 일관성을 위해 잠금 내부에 리턴을 유지하는 것이 분명하기 때문에 잠금 내부에 있어야합니다.


답변

가치가있는 것을 위해 MSDN문서 에는 자물쇠 내부에서 돌아 오는 예가 있습니다. 여기에있는 다른 답변에서 IL과 매우 유사한 것처럼 보이지만 다른 스레드가 반환 변수를 덮어 쓸 위험이 없으므로 잠금 내부에서 반환하는 것이 더 안전 해 보입니다.


답변

동료 개발자가 코드를 쉽게 읽을 수 있도록 첫 번째 대안을 제안합니다.