返回声明应该在锁内还是外?
我只是意识到,在我的代码中的一些地方,我有锁内部的返回语句,有时在外面。 哪一个是最好的?
1)
void example() { lock (mutex) { //... } return myData; }
2)
void example() { lock (mutex) { //... return myData; } }
我应该使用哪一个?
从本质上讲,哪个代码更简单。 单点退出是一个不错的理想,但我不会弯曲代码变形只是为了实现它…而如果替代是声明一个局部variables(锁之外),初始化它(在锁内)和然后返回它(在锁之外),然后我会说,在锁内的一个简单的“返回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;
我肯定会把回报锁在里面。 否则,冒着另一个线程进入锁并在返回语句之前修改你的variables的风险,因此使得原来的调用者接收到不同于预期的值。
如果认为外面的锁看起来更好,但要小心,如果您最终将代码更改为:
return f(...)
如果f()需要被调用,那么它显然需要在锁内部,因为这样保持返回锁内的一致性是有道理的。
这取决于,
我要在这里反对粮食。 我一般会回到里面的锁。
通常variablesmydata是一个局部variables。 我喜欢在初始化时声明局部variables。 我很less有数据在我的锁之外初始化我的返回值。
所以你的比较实际上是有缺陷的。 理想的情况下,两种select之间的区别就像你写的那样,这似乎是对情况1的点头,实际上它有点丑陋。
void example() { int myData; lock (foo) { myData = ...; } return myData }
与
void example() { lock (foo) { return ...; } }
我发现案例2更容易阅读,更难搞砸,特别是对于短片段。
为了让开发人员更容易阅读代码,我会build议第一个select。
对于它的价值, MSDN上的文档有一个从锁内返回的例子。 从这里的其他答案看来,它看起来与IL非常相似,但是对于我来说,从锁内返回似乎更安全一些,因为这样就不会有被另一个线程覆盖的返回variables的风险。
外面看起来更干净