Могу ли я поставить оператор возврата в замке - PullRequest
15 голосов
/ 01 сентября 2009

Dupe: оператор возврата в процедуре блокировки: внутри или снаружи

Название немного вводит в заблуждение. Я знаю, что вы можете сделать это, но мне интересно, как это повлияет на производительность.

рассмотрим эти два блока кода. (без обработки ошибок)

Этот блок имеет return вне замка

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

В этом блоке есть оператор return внутри блокировки

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

         return toReturn;
     }
 }

Есть ли разница в коде? Я понимаю, что различия в производительности (если таковые имеются) будут минимальными, но мне особенно интересно, будет ли разница в порядке, в котором lock получает релиз.

Ответы [ 3 ]

24 голосов
/ 01 сентября 2009

Компилятор C # переместит оператор return за пределы try/finally, созданного для оператора lock. Оба ваших примера идентичны с точки зрения IL, который компилятор выдаст для них.

Вот простой пример, доказывающий, что:

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;
    }
}

Приведенный выше код компилируется в следующее:

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;
        }
}

Как видите, компилятор взял на себя смелость перенести оператор return в Foo за пределы try/finally.

2 голосов
/ 01 сентября 2009

Я полагаю, что IL был бы идентичен ... Я должен был проверить его, чтобы быть уверенным, но оператор блокировки генерирует попытку finally в IL, и возврат вызовет команду finally (с выпуском) ДО кадр стека в любом случае закрывается и возвращается к вызывающей стороне, так что ...

0 голосов
/ 01 сентября 2009

Да, но почему бы не использовать Dequeue?

Помните, что блокировка - это просто сокращение для чего-то вроде:

        try
        {
            Monitor.Enter(QueueModifierLockObject);

            DownloadFile toReturn = queue.Dequeue();         

            return toReturn;
        }
        finally
        {
            Monitor.Exit(QueueModifierLockObject);
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...