我刚刚意识到,在我的代码中的某个地方,我在锁内部有一个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语句之前修改变量的风险,因此使原始调用者收到的值与预期不同。
如果认为外面的锁看起来更好,但是如果你最终将代码改为:
return f(...)
如果需要在保持锁的情况下调用f(),那么它显然需要在锁内部,因此保持在锁内部返回以保持一致性是有意义的。
这取决于,
我要反对这里的粮食。我一般都会回到锁内。
通常变量mydata是局部变量。我喜欢在初始化时声明局部变量。我很少有数据来初始化我的锁定之外的返回值。
所以你的比较实际上是有缺陷的。虽然理想情况下两个选项之间的区别就像你写的那样,这似乎是对案例1的点头,但实际上它有点丑陋。
void example() {
int myData;
lock (foo) {
myData = ...;
}
return myData
}
与
void example() {
lock (foo) {
return ...;
}
}
我发现案例2更容易阅读并且更难搞砸,特别是对于短片段。
为了让其他开发人员更容易阅读代码,我建议第一个替代方案。
对于它的价值,documentation on MSDN有一个从锁内部返回的例子。从这里的其他答案来看,它确实看起来非常相似IL,但对我来说,从锁内部返回似乎更安全,因为那样你就不会冒被另一个线程覆盖的返回变量的风险。
外面看起来更干净
lock() return <expression>
声明总是:
1)输入锁定
2)为指定类型的值创建本地(线程安全)存储,
3)用<expression>
返回的值填充商店,
4)退出锁定
5)退货。
这意味着从lock语句返回的值在返回之前总是“熟”。
不要担心lock() return
,不要听任何人在这里))