2009-08-31 9 views
15

Dupe:return statement in a lock procedure: inside or outsidemogę umieścić return wewnątrz zamka

tytuł jest nieco mylący. Wiem, że możesz to zrobić, ale zastanawiam się nad konsekwencjami wydajności.

rozważ te dwa bloki kodu. (Bez obsługi błędów)

Blok ten ma return poza zamkiem

public DownloadFile Dequeue() 
{ 
    DownloadFile toReturn = null; 
    lock (QueueModifierLockObject) 
    { 
     toReturn = queue[0]; 
     queue.RemoveAt(0); 
    } 
    return toReturn; 
} 

Blok ten posiada oświadczenie return ciągu blokadę

public DownloadFile Dequeue() 
{ 
    lock (QueueModifierLockObject) 
    { 
     DownloadFile toReturn = queue[0]; 
     queue.RemoveAt(0); 

     return toReturn; 
    } 
} 

Czy jest jakaś różnica w kodzie ? Rozumiem, że różnice w wydajności (jeśli występują) byłyby minimalne, ale zastanawiam się, czy nie byłoby różnicy w kolejności, w której lock dostaje zwolnienie.

+1

* Na pewno * zostało to już wcześniej pokryte? – annakata

+0

Nie mogłem znaleźć wyników, ponieważ wcześniej nie korzystałem z odpowiednich wyszukiwanych terminów. Tak, to dupek. – DevinB

Odpowiedz

24

C# kompilator przeniesie return poza try/finally który jest tworzony na rachunku lock. Oba twoje przykłady są identyczne pod względem IL, które kompilator wyemituje dla nich.

Oto prosty przykład dowodzi, że:

class Example 
{ 
    static Object obj = new Object(); 

    static int Foo() 
    { 
     lock (obj) 
     { 
      Console.WriteLine("Foo"); 
      return 1; 
     } 
    } 

    static int Bar() 
    { 
     lock (obj) 
     { 
      Console.WriteLine("Bar"); 
     } 
     return 2; 
    } 
} 

Powyższy kod zostanie skompilowany do następujących:

internal class Example 
{ 
     private static object obj; 

     static Example() 
     { 
       obj = new object(); 
       return; 
     } 

     public Example() 
     { 
       base..ctor(); 
       return; 
     } 

     private static int Bar() 
     { 
       int CS$1$0000; 
       object CS$2$0001; 
       Monitor.Enter(CS$2$0001 = obj); 
     Label_000E: 
       try 
       { 
         Console.WriteLine("Bar"); 
         goto Label_0025; 
       } 
       finally 
       { 
       Label_001D: 
         Monitor.Exit(CS$2$0001); 
       } 
     Label_0025: 
       CS$1$0000 = 2; 
     Label_002A: 
       return CS$1$0000; 
     } 

     private static int Foo() 
     { 
       int CS$1$0000; 
       object CS$2$0001; 
       Monitor.Enter(CS$2$0001 = obj); 
     Label_000E: 
       try 
       { 
         Console.WriteLine("Foo"); 
         CS$1$0000 = 1; 
         goto Label_0026; 
       } 
       finally 
       { 
       Label_001E: 
         Monitor.Exit(CS$2$0001); 
       } 
     Label_0026: 
       return CS$1$0000; 
     } 
} 

Jak widać, kompilator podjął Libery ruchomych powrót oświadczenie w Foo poza numerem try/finally.

+8

Rzeczywiście. A jeśli spojrzysz na wygenerowaną IL, zobaczysz, dlaczego to robimy. Możesz zostawić chroniony region specjalną instrukcją "zostaw", która wie, jak wyczyścić obsługę wyjątków (lub, oczywiście, przez zgłoszenie wyjątku). Nie możesz wykonywać zwykłych oddziałów lub powrotów z chronionego regionu. Dlatego musimy wygenerować liście z chronionego regionu do zwrotu wygenerowanego poza regionem. –

2

Wierzę, że IL byłaby identyczna ... Musiałbym przetestować to by być pewnym, ale oświadczenie blokady generuje próbę ostatecznie w IL, a powrót wyzwalałby ostatecznie (z wydaniem) ZANIM ramka stosu zamknie się i wróci do dzwoniącego, tak, więc ...

0

Tak, ale dlaczego nie skorzystać z opcji Dequeue?

Pamiętaj blokada jest po prostu skrótem zasadniczo coś wzdłuż linii:

 try 
     { 
      Monitor.Enter(QueueModifierLockObject); 

      DownloadFile toReturn = queue.Dequeue();   

      return toReturn; 
     } 
     finally 
     { 
      Monitor.Exit(QueueModifierLockObject); 
     } 
Powiązane problemy