2008-11-05 71 views
111

我剛剛意識到,在我的代碼中的某個地方,我在鎖裏面有一些return語句,並且有時候在外面。哪一個是最好的?返回語句應該在鎖內還是外?

1)

void example() 
{ 
    lock (mutex) 
    { 
    //... 
    } 
    return myData; 
} 

2)

void example() 
{ 
    lock (mutex) 
    { 
    //... 
    return myData; 
    } 

} 

哪一個我應該使用?

+0

如何射擊反射器,並做一些比較IL ;-)。 – 2008-11-05 21:14:20

+6

@Pop:done - 在IL語言中都不是更好 - 僅適用於C#風格 – 2008-11-05 21:32:01

+1

非常有趣,哇,今天我學到了一些東西! – Pokus 2008-11-05 21:35:26

回答

155

從本質上講,它使代碼更簡單。單點退出是一個不錯的理想,但我不會將代碼變形以實現它。如果替代方法是聲明局部變量(在鎖之外),初始化它(在鎖之內)和然後返回它(在鎖之外),然後我會說鎖內的一個簡單的「返回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,但我不會爲任何一方感到興奮。

34

它沒有任何區別;它們都被編譯器翻譯成相同的東西。

爲了澄清,要麼被有效地轉化到一些與以下語義:

T myData; 
Monitor.Enter(mutex) 
try 
{ 
    myData= // something 
} 
finally 
{ 
    Monitor.Exit(mutex); 
} 

return myData; 
+1

那麼,這是真的嘗試/終於 - 然而,鎖外的回報仍然需要額外的本地人,這是無法優化,並採取更多的代碼... – 2008-11-05 21:18:20

+2

你不能從try塊返回;它必須以「.leave」操作碼結束。所以在這兩種情況下排放的CIL應該是相同的。 – 2008-11-05 21:25:46

0

外面看起來更乾淨。

1

爲了讓開發人員更容易閱讀代碼,我會建議第一種選擇。

5

如果想鎖定外面看起來更好,但要小心,如果你最終的代碼更改爲:

return f(...) 

若f()需要與隨後舉行的鎖定它顯然需要內部調用鎖,因爲這樣保持返回鎖內的一致性是有道理的。

4

這取決於,

我要在這裏反對糧食。我通常會回到鎖內。

通常變量mydata是一個局部變量。我喜歡在初始化時聲明局部變量。我很少有數據在鎖之外初始化我的返回值。

所以你的比較實際上是有缺陷的。理想情況下,兩種選擇之間的差異將如您所寫,這似乎給予情況1的點頭,實際上它有點醜陋。

void example() { 
    int myData; 
    lock (foo) { 
     myData = ...; 
    } 
    return myData 
} 

void example() { 
    lock (foo) { 
     return ...; 
    } 
} 

我發現情況2是相當容易閱讀和難以重振旗鼓,尤其是對於短片斷。

30

我肯定會把回報放在鎖內。否則,你可能會冒另一個線程進入鎖並在返回語句之前修改你的變量,從而使原來的調用者收到一個與預期不同的值。

1

對於它的價值,documentation on MSDN有一個從鎖內返回的示例。從這裏的其他答案看來,它看起來與IL非常相似,但對我來說,從鎖內返回似乎更安全,因爲這樣就不會冒另一個線程覆蓋返回變量的風險。

0

lock() return <expression>語句總是:

1)進入鎖定

2)使本地(線程安全的)店指定類型的值,

3)填充存儲與價值通過<expression>返回,

4)出口鎖止

5)返回商店。

這意味着從鎖定語句返回的值在返回之前總是「熟化」。

不要擔心lock() return,不聽任何人在這裏))

相關問題