IT story

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

hot-time 2020. 6. 30. 08:10
반응형

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


방금 내 코드의 어느 곳에서 잠금 내부와 외부에 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
}

So at the IL level they are [give or take some names] identical (I learnt something ;-p). As such, the only sensible comparison is the (highly subjective) law of local coding style... I prefer ReturnInside for simplicity, but I wouldn't get excited about either.


It doesn't make any difference; they're both translated to the same thing by the compiler.

To clarify, either is effectively translated to something with the following semantics:

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

return myData;

I would definitely put the return inside the lock. Otherwise you risk another thread entering the lock and modifying your variable before the return statement, therefore making the original caller receive a different value than expected.


If think the lock outside looks better, but be careful if you end up changing the code to:

return f(...)

If f() needs to be called with the lock held then it obviously needs to be inside the lock, as such keeping returns inside the lock for consistency makes sense.


It depends,

I am going to go against the grain here. I generally would return inside of the lock.

Usually the variable mydata is a local variable. I am fond of declaring local variables while I initialize them. I rarely have the data to initialize my return value outside of my lock.

So your comparison is actually flawed. While ideally the difference between the two options would be as you had written, which seems to give the nod to case 1, in practice its a little uglier.

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

vs.

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

I find case 2 to be considerably easier to read and harder to screw up, especially for short snippets.


To make it easier for fellow developers to read the code I would suggest the first alternative.


For what it's worth, the documentation on MSDN has an example of returning from inside of the lock. From the other answers on here, it does appear to be pretty similar IL but, to me, it does seem safer to return from inside the lock because then you don't run the risk of a return variable being overwritten by another thread.


Outside looks cleaner.


lock() return <expression> statements always:

1) enter lock

2) makes local (thread-safe) store for the value of the specified type,

3) fills the store with the value returned by <expression>,

4) exit lock

5) return the store.

It means that value, returned from lock statement, always "cooked" before return.

Don't worry about lock() return, do not listen to anyone here ))

참고URL : https://stackoverflow.com/questions/266681/should-a-return-statement-be-inside-or-outside-a-lock

반응형