我只是意识到在代码的某些地方,return语句位于锁内部,有时位于外部。哪一个是最好的?
1)
void example()
{
    lock (mutex)
    {
    //...
    }
    return myData;
}
2)
void example()
{
    lock (mutex)
    {
    //...
    return myData;
    }
}
我应该使用哪一个?
我只是意识到在代码的某些地方,return语句位于锁内部,有时位于外部。哪一个是最好的?
1)
void example()
{
    lock (mutex)
    {
    //...
    }
    return myData;
}
2)
void example()
{
    lock (mutex)
    {
    //...
    return myData;
    }
}
我应该使用哪一个?
Answers:
本质上,无论哪种方法都使代码更简单。单点退出是一个很好的理想选择,但是我不会为了实现它而使代码变形……并且如果替代方法是声明局部变量(在锁外),对其进行初始化(在锁内)并然后将其返回(在锁之外),那么我想说,锁内的简单“ return 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为简化起见,我更喜欢,但我都不会对此感到兴奋。
ret在.try区域内。
                    没什么区别;它们都被编译器翻译成同一件事。
为了澄清起见,其中任何一个都有效地转换为具有以下语义的内容:
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
}
与
void example() { 
    lock (foo) {
        return ...;
    }
}
我发现案例2相当容易阅读,而且很难弄乱,特别是对于短片段。
为了使其他开发人员更容易阅读代码,我建议使用第一种方法。
注意:我相信这个答案实际上是正确的,并且希望它也对您有所帮助,但是我总是很乐意根据具体的反馈来改进它。
总结和补充现有的答案:
该接受的答案显示,不论其语法形式的你在你的选择,C#代码,在IL代码-因此在运行时-在return没有发生,直到后锁被释放。
return 放置在lock块中也不能正确表示控制流[1],但从语法上讲,它的方便之处在于它无需将返回值存储在aux中。局部变量(在块外部声明,以便可以return在块外部使用)-请参见Edward KMETT的答案。另外-这是问题的附带内容,但可能仍然很有趣(里卡多·比利亚米尔(Ricardo Villamil)的答案试图解决这个问题,但我认为这是错误的)-将一个lock语句与一个return语句组合在一起-即return在一个受保护的块中获取价值并发访问-仅在获得返回值后实际上不需要保护时才有意义地“保护” 调用方范围内的返回值,这适用于以下情况:
如果返回值是集合中的元素,则仅需要在添加和删除元素方面进行保护,而在修改元素本身和/或... 方面则不需要进行保护。
...如果返回的值是值类型或字符串的实例。
在任何其他情况下,锁定必须由调用者执行,而不是(仅)在方法内部执行。
[1] 西奥多Zoulias指出,在技术上也是如此放置return内try,catch,using,if,while,for,...报表; 但是,lock陈述的具体目的很可能会引起对真正控制流程的审查,这一问题已被问到并引起了广泛关注,这证明了这一点。
[2]访问值类型实例总是会创建它在线程本地的堆栈上的副本;即使字符串从技术上来说是引用类型的实例,它们也有效地表现类似值的类型的实例。
lock,并从return语句的位置得出含义。这是与这个问题恕我直言无关的讨论。另外,我发现“不实陈述”的使用也令人不安。如果从返回lock歪曲控制流,那么同样可以说从一回try,catch,using,if,while,for,和语言的任何其他结构。就像说C#充斥着控制流错误表示。耶稣...
                    try,,if...我个人甚至都不考虑这个问题,但是lock,特别是在的背景下,这个问题对我来说是个问题-如果其他人也没有想到,就永远不会问这个问题,并且接受的答案也不会花很多时间去调查真实的行为。