c # vb: должны ли мы использовать System.Ленивый для ресурсоемкой задачи? (когда многопоточность не нужна)

#c# #.net #vb.net #performance

#c# #.net #vb.net #Производительность

Вопрос:

Мне интересно, происходит ли какой-то JIT-взлом с System.Ленивый, чтобы сделать вещи более производительными или это чисто «обычный класс»?

Со страницы http://msdn.microsoft.com/en-us/library/dd642331.aspx в нем говорится:

Используйте экземпляр Lazy (из T), чтобы отложить создание большого или ресурсоемкого объекта или выполнение ресурсоемкой задачи, особенно когда такое создание или выполнение может не произойти в течение срока службы программы.

но я могу отложить выполнение ресурсоемкой задачи, используя простой логический флаг, не так ли? Итак, в чем именно разница? (кроме System.У Lazy есть дополнительные накладные расходы без видимого увеличения «синтаксического сахара»)

С простым логическим флагом это просто:

 if (!deferred) {
    //run resource-intensive task
}
  

Редактировать:

вот пример

 class Human{
    System.Lazy<String> name = new System.Lazy<String>(() =>
    {
        //code here takes 4 seconds to run
        return "the value";
    });
    String Name
    {
        get
        {
            return name.Value;
        }
    }
}
  

против

 class Human
{
    String name;
    bool name_initiated;
    String Name
    {
        get
        {
            if (!name_initiated)
            {
                //code here takes 4 seconds to run
                name = "the value";
                name_initiated = true;
            }
            return name;
        }
    }
}
  

6 Мая: теперь я часто использую это. И я действительно имею в виду очень много. я использую его всякий раз, когда мне нужно кэшировать данные (даже когда вычисление занимает 0,1 секунды или меньше). Отсюда мой вопрос, должен ли я беспокоиться? Теперь я знаю, что вы скажете мне профилировать приложение, но я сначала создаю библиотеку, прежде чем создавать приложение, и к тому времени, если в приложении возникнут проблемы, это будет означать серьезные изменения

Комментарии:

1. Можете ли вы показать технику, как отложить загрузку / выполнение с помощью простого логического флага? Вы можете начать с en.wikipedia.org/wiki/Call_by_need

2. Я позволю кому-нибудь менее ленивому сформулировать ответ… О, кстати, F # использует Lazy<T> и предоставляет немного синтаксического сахара. let ls = lazy([1;2;3])

3. @Nick я отредактировал вопрос, чтобы показать, как отложить загрузку / выполнение с помощью простого логического флага.

4. @Nick .. ссылка оправдывает использование отложенной оценки, о которой я не спорю. но это не оправдывает использование System. Ленивый вот почему я говорю, что мы можем добиться отложенной оценки с помощью простого логического флага зачем использовать System.Lazy?

5. @Pacerier, но как это там откладывается? Как запускаются вычисления? И какова семантика для этого флага? Выполнять задачу независимо от того, отложена она или нетерпелива? В любом случае, Джон Скит уже разместил хороший ответ. Взгляните. Если вам интересно, почему она вообще существует, взгляните на другую статью о том, как реализовать одноэлементный шаблон: yoda.arachsys.com/csharp/singleton.html это также можно сделать с помощью простого логического флага 😉

Ответ №1:

Да, вы могли бы отложить это с помощью простого логического флага. Конечно, вам нужно было бы обрабатывать изменчивость как флага, так и результата … и убедиться, что вы знали, чего хотите с точки зрения результата, если один поток запрашивает результат, пока он все еще вычисляется. О, и постарайтесь избегать блокировки, где это возможно. И сделать все это пуленепробиваемым с точки зрения потокобезопасности.

Нет, вообще никакой пользы от использования типа, созданного экспертами 😉

Серьезно: зачем делать это самостоятельно, если кто-то другой сделал это за вас? Зачем писать код для проверки флага, разрабатывать, как безопасно ждать, блокировать все и т.д. Даже если бы это было относительно просто сделать правильно, лучше, если это нужно сделать только один раз повторно.

Другим хорошим примером этого принципа является Nullable<T> . Вы могли бы легко получить большую часть того же поведения самостоятельно (не упаковывать) или даже вообще не беспокоиться об инкапсуляции и просто сохранить флаг рядом с вашим обычным полем… но со встроенным типом вы получаете все это реализованным бесплатно, вместе с синтаксическим сахаром и т.д.

Комментарии:

1. @Джон, у тебя есть хорошая статья о реализации singleton csharpindepth.com/Articles/General/Singleton.aspx , вы можете связать это здесь, поскольку singleton также может быть «реализован» с помощью простого логического флага. Но, конечно, это немного оффтопично.

2. @Nick: Ну, вы можете реализовать шаблон singleton с помощью Lazy<T> , но на самом деле это не одно и то же… Я думаю, что предпочел бы не мутить воду в своем ответе, но ссылка все равно есть в комментариях 🙂

3. @Джон, я просто хочу показать: все может быть сложнее, чем это выглядит в обзоре. Singleton (и ваши статья и книга) являются хорошей демонстрацией этого. Конечно, я не думаю Lazy<T> , что с одноэлементным шаблоном то же самое 🙂

4. @Jon Skeet извините, я, наверное, не совсем ясно сформулировал свой вопрос. Я отредактировал свой вопрос на: должны ли мы использовать System. Ленивый для ресурсоемкой задачи (когда многопоточность не требуется)

5. @Pacerier: Контекст вашего вопроса все еще не ясен. Если вы можете показать нам полный пример, мы сможем сказать, Lazy<T> было бы ли это полезно. Это способ представления значения, которое будет вычислено при первой необходимости, а затем может быть использовано повторно. Если вы не находитесь в такой ситуации, это бесполезно.

Ответ №2:

Класс Lazy упрощает процесс. Это похоже на использование строки вместо массива символов. Технически это не обязательно, но может быть полезно.

Комментарии:

1. String на самом деле предоставляет ряд оптимизаций на уровне CLR, которые вы не смогли бы реализовать вне среды выполнения с помощью простого char[] .

2. @dahlbyk: В любом случае, основной смысл использования String заключается в семантической простоте. Вы могли бы выполнить интернирование самостоятельно, если бы захотели, не совсем тем же способом, но вы могли бы получить подавляющую часть производительности, если бы знали, что делаете.

Ответ №3:

Lazy<T> это просто инкапсуляция наилучшего способа реализации ленивого синглтона. Если вы хотите потокобезопасности, это нечто большее, чем просто if(!initialized) instance = Initialize(); . Я обычно предполагаю, что команда BCL будет лучше в реализации, чем я.

Обновление: Основываясь на вашем примере, я бы сказал, что преимущество Lazy<> заключается просто в меньшем количестве кода для поддержки. Помимо этого, они по существу эквивалентны. Мой совет: используйте Lazy<> , потому что это легко, и переходите к более сложным задачам.

Комментарии:

1. извините, я, наверное, не совсем ясно сформулировал свой вопрос. Я отредактировал свой вопрос на: должны ли мы использовать System. Ленивый для ресурсоемкой задачи (когда многопоточность не требуется)

Ответ №4:

Класс Lazy выполняет всю работу по обеспечению потокобезопасности за вас, что является чем-то намного более сложным, чем кажется, для реализации вручную.

Комментарии:

1. извините, я, наверное, не совсем ясно сформулировал свой вопрос. Я отредактировал свой вопрос на: должны ли мы использовать System. Ленивый для ресурсоемкой задачи (когда многопоточность не требуется)