C# return 语句应该在锁内部还是外部?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/266681/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should a return statement be inside or outside a lock?
提问by Patrick Desjardins
I just realized that in some place in my code I have the return statement inside the lock and sometime outside. Which one is the best?
我刚刚意识到,在我的代码中的某个地方,我在锁内有 return 语句,有时在锁外。哪一个是最好的?
1)
1)
void example()
{
lock (mutex)
{
//...
}
return myData;
}
2)
2)
void example()
{
lock (mutex)
{
//...
return myData;
}
}
Which one should I use?
我应该使用哪一种?
采纳答案by Marc Gravell
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.
本质上,无论哪种方式都可以使代码更简单。单点退出是一个很好的理想,但我不会为了实现它而使代码变形......如果替代方案是声明一个局部变量(锁外),初始化它(锁内)和然后返回它(锁外),然后我会说锁内的一个简单的“返回 foo”要简单得多。
To show the difference in IL, lets code:
为了显示 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;
}
}
(note that I'd happily argue that ReturnInside
is a simpler/cleaner bit of C#)
(请注意,我很乐意争辩说这ReturnInside
是一个更简单/更清晰的 C#)
And look at the IL (release mode etc):
并查看 IL(发布模式等):
.method private hidebysig static int32 ReturnInside() cil managed
{
.maxstack 2
.locals init (
[0] int32 CST myData;
Monitor.Enter(mutex)
try
{
myData= // something
}
finally
{
Monitor.Exit(mutex);
}
return myData;
00,
[1] object CSreturn f(...)
01)
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 CSvoid example() {
int myData;
lock (foo) {
myData = ...;
}
return myData
}
00)
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.
因此,在 IL 级别,它们 [给出或取一些名字] 相同(我学到了一些东西;-p)。因此,唯一合理的比较是本地编码风格的(高度主观的)法则......我更喜欢ReturnInside
简单,但我不会对此感到兴奋。
回答by Greg Beech
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:
澄清一下,两者都有效地转换为具有以下语义的内容:
void example() {
lock (foo) {
return ...;
}
}
回答by Ovidiu Pacurar
Outside looks cleaner.
外面看起来更干净。
回答by Adam Asham
To make it easier for fellow developers to read the code I would suggest the first alternative.
为了让其他开发人员更容易阅读代码,我建议第一种选择。
回答by Rob Walker
If think the lock outside looks better, but be careful if you end up changing the code to:
如果认为外面的锁看起来更好,但如果最终将代码更改为:
##代码##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.
如果 f() 需要在持有锁的情况下调用,那么它显然需要在锁内,因此为了一致性而将返回保持在锁内是有意义的。
回答by Edward KMETT
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.
通常变量 mydata 是一个局部变量。我喜欢在初始化它们时声明局部变量。我很少有数据来初始化我的锁之外的返回值。
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.
所以你的比较实际上是有缺陷的。理想情况下,这两个选项之间的区别与您所写的一样,这似乎是对案例 1 的认可,但实际上它有点丑陋。
##代码##vs.
对比
##代码##I find case 2 to be considerably easier to read and harder to screw up, especially for short snippets.
我发现案例 2 更容易阅读并且更难搞砸,尤其是对于短片。
回答by Ricardo Villamil
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.
我肯定会把退货放在锁里。否则,您可能会冒着另一个线程在 return 语句之前进入锁并修改变量的风险,从而使原始调用者收到与预期不同的值。
回答by greyseal96
For what it's worth, the documentation on MSDNhas 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.
对于它的价值,MSDN上的文档有一个从锁内部返回的示例。从这里的其他答案来看,它似乎与 IL 非常相似,但对我来说,从锁内部返回似乎更安全,因为这样您就不会冒返回变量被另一个线程覆盖的风险。
回答by mshakurov
lock() return <expression>
statements always:
lock() return <expression>
声明总是:
1) enter lock
1)输入锁
2) makes local (thread-safe) store for the value of the specified type,
2) 使本地(线程安全)存储指定类型的值,
3) fills the store with the value returned by <expression>
,
3) 用返回的值填充存储<expression>
,
4) exit lock
4)退出锁
5) return the store.
5)返回商店。
It means that value, returned from lock statement, always "cooked" before return.
这意味着从 lock 语句返回的值总是在返回之前“煮熟”。
Don't worry about lock() return
, do not listen to anyone here ))
别担心lock() return
,不要听这里的任何人))
回答by mklement0
Note: I believe this answer to be factually correct and I hope that it is helpful too, but I'm always happy to improve it based on concrete feedback.
注意:我相信这个答案在事实上是正确的,我希望它也有帮助,但我总是很乐意根据具体的反馈来改进它。
To complement the existing answers:
补充现有的答案:
The accepted answershows that, irrespective of which syntax form you choose in your C#code, in the IL code - and therefore at runtime - the
return
doesn't happen until afterthe lock is released.- Even though placing the
return
insidethelock
block therefore, strictly speaking, misrepresents the flow of control[1], it is syntactically convenientin that it obviates the need for storing the return value in an aux. local variable (declared outside the block, so that it can be used with areturn
outside the block).
- Even though placing the
Separately - and this aspect is incidentalto the question, but may still be of interest - combining a
lock
statement with areturn
statement - i.e., obtaining value toreturn
in a block protected from concurrent access - only meaningfully "protects" the returned value in the caller's scope if it doesn't actually need protecting once obtained, which applies in the following scenarios:If the returned value is an element from a collection that only needs protection in terms of adding and removingelements, not in terms of modifications of the elements themselvesand/or ...
... if the value being returned is an instance of a value typeor a string.
- Note that in this case the caller receives a snapshot(copy)[2]of the value - which by the time the caller inspects it may no longer be the current value in the data structure of origin.
In any other case, the locking must be performed by the caller, not (just) inside the method.
该接受的答案显示,不论其语法形式的你在你的选择,C#代码,在IL代码-因此在运行时-在
return
没有发生,直到后锁被释放。- 即使将所述
return
内部的lock
块因此,严格来说,歪曲控制流[1] ,它在语法上是方便的,因为它避免了对在一个辅助存储返回值的需要。局部变量(在块外部声明,以便它可以与return
块外部一起使用)。
- 即使将所述
单独 - 这方面是问题的附带,但可能仍然令人感兴趣 - 将
lock
语句与return
语句组合 - 即,return
在受保护的块中获取值以防止并发访问 - 仅有意义地“保护”调用者中的返回值' s 范围,如果它一旦获得实际上不需要保护,则适用于以下场景:如果返回值是集合中的一个元素,该元素只需要在添加和删除元素方面进行保护,而不是在修改元素本身和/或...
...如果返回的值是值类型或字符串的实例。
- 请注意,在这种情况下,调用者会收到值的快照(副本)[2]- 当调用者检查时,它可能不再是原始数据结构中的当前值。
在任何其他情况下,锁定必须由调用者执行,而不是(仅)在方法内部执行。
[1] Theodor Zouliaspoints out that that is technically also true for placing return
inside try
, catch
, using
, if
, while
, for
, ... statements; however, it is the specific purpose of the lock
statement that is likely to invite scrutiny of the true control flow, as evidenced by this question having been asked and having received much attention.
[1] Theodor Zoulias指出,这在技术上也适用于放置return
在try
, catch
, using
, if
, while
, for
, ... 语句中;然而,lock
声明的特定目的可能会引起对真实控制流的审查,这一点已被提出并受到很多关注就证明了这一点。
[2] Accessing a value-type instance invariably creates a thread-local, on-the-stack copy of it; even though strings are technically reference-type instances, they effectively behave like-value type instances.
[2] 访问一个值类型实例总是会创建一个线程本地的、堆栈上的副本;尽管字符串在技术上是引用类型的实例,但它们实际上表现得像值类型的实例。