Могу ли я поставить оператор возврата внутри блокировки

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

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

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

Этот блок имеет 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 получит выпуск.

Ответы

Ответ 1

Компилятор С# перемещает оператор return за пределами try/finally, который создается для оператора lock. Оба ваших примера идентичны с точки зрения ИЛ, которые компилятор будет использовать для них.

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

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

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

Ответ 3

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

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

        try
        {
            Monitor.Enter(QueueModifierLockObject);

            DownloadFile toReturn = queue.Dequeue();         

            return toReturn;
        }
        finally
        {
            Monitor.Exit(QueueModifierLockObject);
        }