Should a return statement be inside or outside a lock?
Essentially, which-ever makes the code simpler. Single point of exit is a nice ideal, but I wouldn't bend the code out of shape just to achieve it... And if the alternative is declaring a local variable (outside the lock), initializing it (inside the lock) and then returning it (outside the lock), then I'd say that a simple "return foo" inside the lock is a lot simpler.
To show the difference in IL, lets code:
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; }}
(note that I'd happily argue that ReturnInside
is a simpler/cleaner bit of C#)
And look at the IL (release mode etc):
.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.