Хорошо, всё те же условия — сервер с неравномерной загрузкой. Запросы могут идти как по одному, так и большими «пачками».
Решение:
Когда приходит запрос, проверяется по таймеру состояние объекта context и, если он не готов к работе, вызывается метод Expose, после чего включается таймер, например, на секунд 10. Если запросов больше не было, то спустя этот промежуток вызывается Dispose, иначе таймер продлевается. В результате соединение слишком долго не держится и не нужно постоянно создавать и удалять context. По-моему, по такой схеме вполне можно получить выигрыш в производительности. Не проверял это на практике, но исхожу из логических рассуждений.
А кто-нибудь, кроме вас, его признает?
Моя задача показать идею. Возможно, кто-то уже доходил до неё, но в других источниках её описания мне не встречалось.
В примерах просто не возникло необходимости в IDisposable. Хотя вполне могут иметь место сценарии, где вью-модель занимает какие-то ресурсы, которые нужно освобождать.
Exposable и Disposable применимы как раздельно, так и совместно — зависит от ситуации. Внимания заслуживает тот факт, что оба паттерна обратно симметричны относительно жизненного цикла объекта.
Но, конечно, в каждом конкретном случае существует возможность применять более хитрые механизмы для подобных целей.
Реальных измерений производительности в описанных сценариях у меня нет, тут могут оказывать влияние многие факторы, например, ресурсоёмкость объекта DbContext, наличие свободной оперативной памяти, количество и временное распределение запросов. Суть в том, чтобы показать, что существует такой паттерн Exposable обратный Disposable и их возможно применять как по раздельности, так и вместе.
Ради интереса замерил разницу в скороти чтения и записи на реализациях INotifyPropertyChanged с лямбда-выражениями и без них.
Получившиеся результаты:
По скороти доступа (get) классическая реализация выигрывает примерно в 100 раз
По записи (set) классическая лучше примерно в 20 раз
На результаты немного также влияет упаковка-распаковка численных значений. В лямбда-реализациях числа хранятся в виде объектов (упакованы), поэтому сразу может показаться, что это хуже. Однако если вдуматься, UI-привязки работают через рефлексию, поэтому, скорее всего, в классических реализациях упаковка происходит неявно. Тут зависит от конкретного случая, где и как используется свойство чаще, в коде вью-модели или на UI.
Разница в 100 и 20 раз выглядит внушительно, но на деле это оказываются наносекунды (скорость записи, более медленная операция, у меня получилась около 2нс для лямбд, но зависит от производительности устройства). Частота же обносвления экрана в 100 Гц эквивалентна10 мс, поэтому в реальных приложениях на FPS это оказывает ничтожное влияние, если вообще оказывает. Экономятся лишь такты процессора.
В плане красоты разработки и гибкости лямбда-выражения выигрывают, а действительно значимого падения производительности никак не происходит в большинстве обычных случаев, поэтому боятся их уж точно не стоит, это факт.
Также довольно полезен бывает метод Skip. Например, если нужно взять десять минимальных элементов, но не первых, а, скажем, начиная с сотого, то операция будет выглядеть так:
persons.OrderBy(p=>p.Age).Skip(100).Take(10)
Вообще же в Linq предусмотрены методы на многие случаи из жизни, поэтому прежде чем использовать свои лучше заглянуть в документацию. Хотя свои методы делать тоже полезно для развития мышления, сразу дойти до оптимального решения обычно трудно, а многое уже придумано до нас.
Всё же решил убрать это утверждение из статьи, поскольку оно спорное.
Не мной оно было придумано, но, к большому сожалению, не удалось найти тот первоисточник, где это изначально встретил.
Конечно, сложно судить о производительности и затратах памяти ваших методов, не имея перед глазами их полной реализации. Возможно, вы применяете какой-то хитрый алгоритм оптимизированный для работы с вещественными числами, большими или маленькими коллекциями — всякое допустимо.
Но по опыту могу сказать, что зачастую безопаснее использовать стандартные механизмы. Хотя раньше сам решал подобные задачи в лоб, не подозревая даже, что для этого годятся OrderBy, Take и другие методы из этой серии :)
Сразу прошу извинить меня, если несу чушь, но…
Насколько мне известно, переменные делегатов являются неизменяемыми типами (immutable types), как структуры, то есть во время присваиваивания создаётся новая копия данных, а не просто присваивается ссылка. Вот не знаю только, происходит ли это атомарно.
Структуры копируются при присваивании. При присваивании структуры к новой переменной выполняется копирование всех данных, а любое изменение новой копии не влияет на данные в исходной копии. Это важно помнить при работе с коллекциями типов значений, такими как Dictionary<string, myStruct>.
Мне нравится полиморфизм и вариант с индексатором :)
Стандартных подсказок достаточно, чтобы ни в чём не запутаться. Зато вью-модели получаются такими, что любо глянуть.
Исходный код библиотеки полностью открыт, поэтому при желании можно реализовать всё на свой вкус!
Долго искал, но, к сожалению, не нашёл того комментария, где когда-то читал о событиях и сборщике мусора. Или мне приснилось?
В общем, проблема, потенциально, может возникнуть в том случае, если операция копирования значения в переменную выполняется не атомарно (var handler = Handler) и поток прервётся не докопировав всё как положено, а за это время кто-то успеет отписаться и вызвать сборщик мусора. После этого управление вернётся исходному потоку и копирование завершиться, но исходный объект будет уничтожен сборщиком мусора, что, очевидно, приведёт к исключению.
Тут всё зависит от диспетчеризации потоков, поэтому вручную такое весьма сложно воспроизвести. И если действительно проблема существует, то она крайне трудноуловима.
Я ожидаю, что в хорошей литературе тут же будет дан оригинальный английский термин, как в википедии
В том-то и дело, что новый термин может встретиться в любом месте, например, на форуме, и человеку захочется с ним разобраться. Первым делом он начнёт поиски с наиболее предсказуемых вариантов перевода.
Конечно, методом проб и ошибок, скорее всего, он найдёт, что нужно, однако если слова из разных языков схожи, то этот процесс произойдёт быстрее. Да общей путаницы в понятиях будет меньше.
Например, оригинальное название статьи с MSDN — Declarators and Variable Declarations. Конечно, русский язык богат, и можно подобрать множество синонимов к слову Declaration, но всё-таки, на мой взгляд, хорошо, когда различные термины более-менее универсальны и созвучны на разных языках. Это моё личное мнение, которого придерживаюсь при написании статей.
А я и есть реалист, потер из своего кода, после того как в определенный момент профайлер показал мне наглядно, что я не прав!
Наверно мне везёт, но пока ни разу не возникало хоть сколько-нибудь заметных тормозов из-за лямбд, какие бы они медленные ни были в синтетических тестах.
А какой смысл подписываться на свое событие? Это оверхед на ровном месте, разве нет?
this.PropertyChanged += (o, e) =>
{
if (e.PropertyName != "Name") return;
// do something
};
Смысл в том, чтобы избавиться от if-оператора и получить более компактную запись. На практике порой хватает одной-двух строк кода вмето, как минимум, четырёх (при условии стандартного форматирования).
Паттерн IDataErrorInfo работает через индексатор и это выглядит довольно красиво. На этой почве возникла идея неявных индексных свойств, о которых можно прочитать в документации к библиотеке, где ключом так же является строковое имя свойства (полиморфизм в действии). После этого очень логичным шагом показалась перегрузка индексатора для возможности подписки на изменение значений свойств (INotifyPropertyChanged). Кроме того существует перегрузка индексатора и для команд (ICommand).
На мой взгляд, всё это выглядит очень эстетично и стройно. Во-первых, модификатор this часто опускается и его редко видно, а тут ему нашлось интересное применение, во-вторых же, синтаксис подсказывает, что операция производится с текущим объектом.
если модель составная и описывает список вложенных моделей, то новый индексатор будет семантически конфликтовать с существующим
Не совсем понял, что вы имеете здесь в виду.
и заранее продумать архитектуру, чтобы для каждого свойства, изменение которого планируется отслеживать снаружи, было отдельное событие.
В текущей реализации для каждого отслеживаемого свойства создаётся отдельный обработчик как снаружи, так и изнутри.
Будем реалистами :) все эти задержки в большинстве случаев настолько ничтожны, что преимущества от их испольования затмевают собой недостатки. Тем более ничто не запрещает комбинировать разные подходы между собой, где это на самом деле нужно.
А вот подписка на событие с помощью лямды, это почти гарантированная утечка памяти!
Поподробнее, что вы имеете в виду? Да, как и любые другие подписки они могут привести к утечкам памяти.
Вызов обработчика у отписавшегося объекта воспроизводится легко (например, достаточно поставить вызов Thread.Sleep() перед handler(o, e)). Экспериментально установлено, что var handler = Handler удерживает оба объекта от сборки мусора даже если других ссылок на объекты не осталось, но, теоретически, как мне думается, операция handler = Handler может произойти не атомарно, и если в этот короткий промежуток времени произойдёт отписка и сборка мусора, то в дальнейшем получится исключение. К сожалению, такое воспроизвести сложно.
Спасибо, хорошая рекомендация. Только она подойдёт лишь для Desktop-версии библиотеки. В Portable-варианте перечисление MethodImplOptions включает только два флага NoInlining и NoOptimization. Возможно, компилятор самостоятельно производит Inlining, где это нужно.
Например, начинающий разработчик встретился с понятием внедрение зависимостей и хочет найти английскую литературу по этому вопросу. Для этого нужно сформулировать соответствующий запрос. Но, например, гугл предлагает следующие переводы для слова внедрение: introduction, implantation, plantation, inculcation, intrusion, intercalation, infiltration.
Среди них даже не встречается injection, которое состоит в изначальном понятии Dependency Injections, поэтому в переводах технических терминов иногда проще не уходить далеко от оригинала. Да и это помогает легче запоминать иностранные слова.
Решение:
Когда приходит запрос, проверяется по таймеру состояние объекта context и, если он не готов к работе, вызывается метод Expose, после чего включается таймер, например, на секунд 10. Если запросов больше не было, то спустя этот промежуток вызывается Dispose, иначе таймер продлевается. В результате соединение слишком долго не держится и не нужно постоянно создавать и удалять context. По-моему, по такой схеме вполне можно получить выигрыш в производительности. Не проверял это на практике, но исхожу из логических рассуждений.
Моя задача показать идею. Возможно, кто-то уже доходил до неё, но в других источниках её описания мне не встречалось.
Exposable и Disposable применимы как раздельно, так и совместно — зависит от ситуации. Внимания заслуживает тот факт, что оба паттерна обратно симметричны относительно жизненного цикла объекта.
Но, конечно, в каждом конкретном случае существует возможность применять более хитрые механизмы для подобных целей.
Реальных измерений производительности в описанных сценариях у меня нет, тут могут оказывать влияние многие факторы, например, ресурсоёмкость объекта DbContext, наличие свободной оперативной памяти, количество и временное распределение запросов. Суть в том, чтобы показать, что существует такой паттерн Exposable обратный Disposable и их возможно применять как по раздельности, так и вместе.
Получившиеся результаты:
По скороти доступа (get) классическая реализация выигрывает примерно в 100 раз
По записи (set) классическая лучше примерно в 20 раз
На результаты немного также влияет упаковка-распаковка численных значений. В лямбда-реализациях числа хранятся в виде объектов (упакованы), поэтому сразу может показаться, что это хуже. Однако если вдуматься, UI-привязки работают через рефлексию, поэтому, скорее всего, в классических реализациях упаковка происходит неявно. Тут зависит от конкретного случая, где и как используется свойство чаще, в коде вью-модели или на UI.
Разница в 100 и 20 раз выглядит внушительно, но на деле это оказываются наносекунды (скорость записи, более медленная операция, у меня получилась около 2нс для лямбд, но зависит от производительности устройства). Частота же обносвления экрана в 100 Гц эквивалентна10 мс, поэтому в реальных приложениях на FPS это оказывает ничтожное влияние, если вообще оказывает. Экономятся лишь такты процессора.
В плане красоты разработки и гибкости лямбда-выражения выигрывают, а действительно значимого падения производительности никак не происходит в большинстве обычных случаев, поэтому боятся их уж точно не стоит, это факт.
Вообще же в Linq предусмотрены методы на многие случаи из жизни, поэтому прежде чем использовать свои лучше заглянуть в документацию. Хотя свои методы делать тоже полезно для развития мышления, сразу дойти до оптимального решения обычно трудно, а многое уже придумано до нас.
Не мной оно было придумано, но, к большому сожалению, не удалось найти тот первоисточник, где это изначально встретил.
Но по опыту могу сказать, что зачастую безопаснее использовать стандартные механизмы. Хотя раньше сам решал подобные задачи в лоб, не подозревая даже, что для этого годятся OrderBy, Take и другие методы из этой серии :)
Насколько мне известно, переменные делегатов являются неизменяемыми типами (immutable types), как структуры, то есть во время присваиваивания создаётся новая копия данных, а не просто присваивается ссылка. Вот не знаю только, происходит ли это атомарно.
MSDN
Стандартных подсказок достаточно, чтобы ни в чём не запутаться. Зато вью-модели получаются такими, что любо глянуть.
Исходный код библиотеки полностью открыт, поэтому при желании можно реализовать всё на свой вкус!
В общем, проблема, потенциально, может возникнуть в том случае, если операция копирования значения в переменную выполняется не атомарно (var handler = Handler) и поток прервётся не докопировав всё как положено, а за это время кто-то успеет отписаться и вызвать сборщик мусора. После этого управление вернётся исходному потоку и копирование завершиться, но исходный объект будет уничтожен сборщиком мусора, что, очевидно, приведёт к исключению.
Тут всё зависит от диспетчеризации потоков, поэтому вручную такое весьма сложно воспроизвести. И если действительно проблема существует, то она крайне трудноуловима.
В том-то и дело, что новый термин может встретиться в любом месте, например, на форуме, и человеку захочется с ним разобраться. Первым делом он начнёт поиски с наиболее предсказуемых вариантов перевода.
Конечно, методом проб и ошибок, скорее всего, он найдёт, что нужно, однако если слова из разных языков схожи, то этот процесс произойдёт быстрее. Да общей путаницы в понятиях будет меньше.
Например, оригинальное название статьи с MSDN — Declarators and Variable Declarations. Конечно, русский язык богат, и можно подобрать множество синонимов к слову Declaration, но всё-таки, на мой взгляд, хорошо, когда различные термины более-менее универсальны и созвучны на разных языках. Это моё личное мнение, которого придерживаюсь при написании статей.
Не совсем понимаю, что вы имеете в виду.
Конструкция
эквивалентна
Смысл в том, чтобы избавиться от if-оператора и получить более компактную запись. На практике порой хватает одной-двух строк кода вмето, как минимум, четырёх (при условии стандартного форматирования).
На мой взгляд, всё это выглядит очень эстетично и стройно. Во-первых, модификатор this часто опускается и его редко видно, а тут ему нашлось интересное применение, во-вторых же, синтаксис подсказывает, что операция производится с текущим объектом.
Не совсем понял, что вы имеете здесь в виду.
В текущей реализации для каждого отслеживаемого свойства создаётся отдельный обработчик как снаружи, так и изнутри.
Поподробнее, что вы имеете в виду? Да, как и любые другие подписки они могут привести к утечкам памяти.
Соглашусь, что с первым вариантом внешней подписки нужно быть внимательным, но второй замкнутый вполне безопасен.
Вызов обработчика у отписавшегося объекта воспроизводится легко (например, достаточно поставить вызов Thread.Sleep() перед handler(o, e)). Экспериментально установлено, что var handler = Handler удерживает оба объекта от сборки мусора даже если других ссылок на объекты не осталось, но, теоретически, как мне думается, операция handler = Handler может произойти не атомарно, и если в этот короткий промежуток времени произойдёт отписка и сборка мусора, то в дальнейшем получится исключение. К сожалению, такое воспроизвести сложно.
Возможно, конечно, и ошибаюсь в чём-то :)
Например, начинающий разработчик встретился с понятием внедрение зависимостей и хочет найти английскую литературу по этому вопросу. Для этого нужно сформулировать соответствующий запрос. Но, например, гугл предлагает следующие переводы для слова внедрение: introduction, implantation, plantation, inculcation, intrusion, intercalation, infiltration.
Среди них даже не встречается injection, которое состоит в изначальном понятии Dependency Injections, поэтому в переводах технических терминов иногда проще не уходить далеко от оригинала. Да и это помогает легче запоминать иностранные слова.