.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День сто семьдесят третий. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
10. Что такое сборщик мусора?
Мусор состоит из объектов, созданных во время выполнения программы в управляемой куче, которые больше не доступны программе.
Сборщик мусора (Garbage Collector - GC) является частью .NET Framework, которая выделяет и освобождает память для ваших приложений .NET. CLR управляет выделением и освобождением управляемого объекта в памяти. Программисты C# никогда не делают этого напрямую, этим управляет сборщик мусора.
Объекты .NET размещаются в области памяти, называемой управляемой кучей. Они будут автоматически уничтожены сборщиком мусора. Распределение кучи происходит только при создании экземпляров классов. Это избавляет программиста от необходимости вручную удалять объекты, которые больше не требуются для выполнения программы. Такое повторное использование памяти помогает уменьшить объем памяти, необходимый программе для работы. Объекты размещаются в куче непрерывно, один за другим. Это очень быстрый процесс, так как это просто добавление значения к указателю.
Процесс освобождения памяти называется сборкой мусора. Он освобождает только те объекты, которые больше не используются в приложении. Корень - это место в хранилище, содержащее ссылку на объект в управляемой куче. CLR проверяет объекты в управляемой куче, чтобы определить, доступны ли они из приложения (имеют ли корень), и создает граф доступных объектов в куче.
Предположим, что управляемая куча содержит набор объектов с именами A, B, C, D, E, F и G (см. рисунок ниже). Во время сборки мусора эти объекты проверяются на наличие активных корней. После построения графа недоступные объекты (в данном случае C и F) помечаются как мусор.
После этого объекты помеченные, как мусор удаляются из памяти. В этот момент оставшееся пространство в куче сжимается, что, в свою очередь, заставляет CLR изменить набор активных корней приложения, чтобы обращение происходило к правильному месту в памяти. Наконец, указатель следующего объекта изменяется, чтобы указывать на следующее свободное место.

Поколения объектов
Для повышения производительности освобождения памяти управляемая куча разделена на сегменты, называемые «поколениями». Существует 3 поколения: 0, 1 и 2. Идея поколений проста: чем дольше объект существует, тем дольше он доступен и тем реже сборщик мусора будет проверять его корень.
Когда объекты только что созданы, они помещаются в Поколение 0 (Gen 0).
Когда Gen 0 заполняется, GC выполняет сборку мусора. При этом удаляются все недоступные объекты из кучи. Все доступные объекты переходят в Поколение 1 (Gen 1). Сбор мусора в Gen 0 - довольно быстрая операция.
Когда Gen 1 заполняется, выполняется сборка мусора Gen 1. Все объекты, которые сохранились после сборки мусора, повышаются до Gen 2. При этом также выполняется сборка в Gen 0.
Когда заполнено Gen 2, GC выполняет полную сборку мусора. Сначала выполняется сборка в Gen 2, затем в Gen 1 и Gen 0. Если памяти для новых объектов всё ещё недостаточно, GC выбрасывает исключение OutOfMemory.
Во время полной сборки мусора GC должен пройти через все объекты в куче, поэтому этот процесс может оказать большое влияние на системные ресурсы.

Источник: https://www.c-sharpcorner.com
День сто семьдесят четвёртый. #BestPractices
Когда использовать LINQ с синтаксисом запроса вместо методов. Начало
Выражения LINQ можно написать в двух вариантах - синтаксис запроса и синтаксис методов. Например, следующие запросы идентичны:
var numbers = Enumerable.Range(1, 100); //1,2,...,100
//Запрос:
var query = from n in numbers
where n % 3 == 0
select n * 2;
//Методы:
var method = numbers
.Where(n => n % 3 == 0)
.Select(n => n * 2);

По некоторым причинам большинство разработчиков (включая меня) чувствуют себя более комфортно с синтаксисом методов. Возможно, причина в том, что программисты привыкли к регулярным вызовам методов. В отличие от синтаксиса запроса, который является своего рода новым языком (ну, вроде как SQL, но он все же менее привычен, чем C#).
Нет никакого явного преимущества одного синтаксиса над другим. Фактически любой синтаксис запроса может быть преобразован в синтаксис методов.
Однако есть несколько случаев, когда синтаксис запроса лучше, то есть он делает код более читабельным:

1. Ключевое слово "let"
Ключевое слово let позволяет сохранить результат для последующего использования в запросе. Вот пример:
var querySyntax =
from person in people
let yearsWorking = GetYearsWorking(person)
where yearsWorking > 4
orderby yearsWorking
select person.Name;

var methodSyntax = people
.Select(person => new {
YearsWorking = GetYearsWorking(person), Name = person.Name })
.Where(x => x.YearsWorking > 4)
.OrderBy(x => x.YearsWorking)
.Select(x => x.Name);
Как видите, с синтаксисом запросов все красиво и чисто. Синтаксис метода не является ужасным, но он требует создания анонимные классов и использования их в остальной части запроса. Поэтому, когда вы хотите «сохранить» значение в дополнение к запрашиваемой коллекции, рассмотрите возможность использования предложения let.

2. OrderBy по нескольким полям
Как в синтаксисе запроса, так и в синтаксисе методов вы можете легко выполнить упорядочивание по нескольким полям. Например, мы можем упорядочить людей по возрасту, а затем по доходу, где возраст - первый, а доход - второй. Это означает, что люди с одинаковым возрастом будут упорядочены по доходам:
var people = new Person[]
{
new Person() {Age = 20, Income = 5000, Name = "Peter"},
new Person() {Age = 30, Income = 8000, Name = "Alfredo"},
new Person() {Age = 30, Income = 7000, Name = "Bo"},
new Person() {Age = 20, Income = 4000, Name = "Jo"},
new Person() {Age = 20, Income = 6000, Name = "Amanda"},
new Person() {Age = 30, Income = 5500, Name = "Maxim"},
};

var querySyntax = from person in people
orderby person.Age, person.Income
select $"{person.Age} {person.Income} {person.Name}";
var methodSyntax = people
.OrderBy(person => person.Age)
.ThenBy(person => person.Income)
.Select(person => $"{person.Age} {person.Income} {person.Name}");

//result
//20 4000 Jo
//20 5000 Peter
//20 6000 Amanda
//30 5500 Maxim
//30 7000 Bo
//30 8000 Alfredo
Я признаю, что оба варианта хороши, и разница не так велика, как в других случаях. Но синтаксис запроса все же выглядит лучше.

Продолжение следует…

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

Источник:
https://michaelscodingspot.com/
👍2
День сто семьдесят пятый. #BestPractices
Когда использовать LINQ с синтаксисом запроса вместо методов. Продолжение

3. Несколько источников данных
Если у вас есть несколько источников данных для запроса, синтаксис запроса будет предпочтительнее. Причина в том, что вы можете использовать ключевое слово from несколько раз. Например:
var rows = Enumerable.Range(1, 3); //1,2,3
var columns = new string[] { "A", "B"};
var querySyntax = from row in rows
from col in columns
select $"cell [{row}, {col}]";
var methodSyntax =
rows.SelectMany(row => columns, (r, c) => $"cell [{r}, {c}]");
foreach (var cell in methodSyntax)
{
Console.WriteLine(cell);
}
//Вывод:
//cell[1, A]
//cell[1, B]
//cell[2, A]
//cell[2, B]
//cell[3, A]
//cell[3, B]
Цель здесь - получить коллекцию со всеми возможными комбинациями из 2 источников. С синтаксисом запроса код прост и не требует пояснений. А синтаксис методов вовсе не так очевиден для понимания.

4. GroupBy или group
Метод расширения GroupBy очень похож на group в запросе. Например:
var names = new string[] { "Alex", "George", "Alfredo", "Bo", "Greg", "Maxim" };
var querySyntax = from name in names
group name by name[0];
var methodSyntax = names
.GroupBy(name => name[0], name => name);
foreach (var pair in querySyntax)
{
var names1 = string.Join(", ", pair.ToList());
Console.WriteLine($"Key = {pair.Key} Names = {names1}");
}
//Вывод:
//Key = A Names = Alex, Alfredo
//Key = G Names = George, Greg
//Key = B Names = Bo
//Key = M Names = Maxim
Опять же, синтаксис методов не совсем ясен. Что означает второй параметр? Конечно, если немного подумать, смысл становится понятен. Но я не хочу думать, когда я смотрю на код, я хочу читать его как книгу. Детскую книгу, если это возможно.

5. Объединения
Как правило, всякий раз, когда вам нужно объединить коллекции, синтаксис запроса будет более читаемым. Вот внутреннее объединение, например:
var categories = new Category[]
{
new Category() {Name="Игрушки", ID=1},
new Category() {Name="Приборы", ID=2}
};
var products = new Product[]
{
new Product(){Name="Кукла", CategoryID=1},
new Product(){Name="Блендер", CategoryID=2},
new Product(){Name="Утюг", CategoryID=2},
new Product(){Name="Медведь", CategoryID=1}
};
var querySyntax =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new { ProductName = prod.Name, Category = category.Name };
var methodSyntax = categories.Join(products,
category => category.ID,
prod => prod.CategoryID,
(category, prod) => new {ProductName = prod.Name, Category = category.Name});
В синтаксисе методов сравниваются 2-й и 3-й параметры объединения. Но в отличие от синтаксиса запроса, это не совсем понятно из кода. В синтаксисе запросов это намного понятнее.

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

Источник:
https://michaelscodingspot.com/
👍1
День сто семьдесят шестой. #юмор
День сто семьдесят седьмой. #ЧтоНовенького
Повысьте Уровень Знаний о VS 2019 с Pluralsight
С запуском Visual Studio 2019 в апреле Microsoft объявили о партнерстве с Pluralsight для предоставления нового учебного контента, который поможет вам развить свои навыки в Visual Studio 2019. Теперь доступны все 10 курсов (более 14 часов контента) о Visual Studio 2019 на Pluralsight. Вы также сможете оценить свой текущий уровень квалификации с оценкой IQ Pluralsight Skill, которая предложит рекомендации по курсу, а после, по мере прохождения курса, вы можете оценить свой прогресс.
10 видеокурсов разделены на категории по уровням подготовки:
1. Новичок
Эти курсы разработаны, чтобы помочь вам освоить наиболее распространенные действия в Visual Studio. Если вы использовали предыдущие версии Visual Studio, ознакомьтесь с курсом «What’s New in Visual Studio 2019», чтобы быстро освоить все новые функции. Если вы новичок в Visual Studio, вам нужно пройти курс «Visual Studio 2019 Getting Started». Затем улучшите свои навыки отладки на курсе «Visual Studio 2019: Debugging».
2. Средний
Отточите свои навыки с помощью курсов из этого раздела, и научитесь управлять своими репозиториями Git с помощью Visual Studio. Вы можете узнать о возможностях тестирования, доступных в Visual Studio, функциях анализа кода и о том, как Visual Studio помогает переносить решения в Azure:
- Using Git for Source Control in Visual Studio 2019
- Testing .NET Code in Visual Studio 2019
- Code Analysis in Visual Studio 2019
- Migrating Applications and Services to Azure with Visual Studio 2019
3. Продвинутый
Выделитесь среди других знаниями, которые вы получите в этом разделе. Всего за полчаса вы можете научиться пользоваться Visual Studio Live Share. Узнайте о расширенных функциях отладки и о том, как создавать собственные облачные решения в Visual Studio.
- Play by Play: Visual Studio Live Share
- Advanced Debugging with Visual Studio 2019
- Building Cloud-native Solutions for Azure with Visual Studio

Курс доступен здесь: https://app.pluralsight.com/paths/skills/visual-studio-2019

Замечания:
- Курсы (естественно) на английском.
- Подписчики Visual Studio получают бесплатный доступ ко всем курсам Pluralsight на 6 месяцев. А курс по Visual Studio, судя по всему, бесплатный.

Источник: https://devblogs.microsoft.com/visualstudio/skill-up-on-visual-studio-2019-with-pluralsight/
This media is not supported in your browser
VIEW IN TELEGRAM
День сто семьдесят восьмой. #ЧтоНовенького
Что Нового в Visual Studio 16.2
Visual Studio 16.2 стала доступна для обычной загрузки. Вот некоторые обновления в этой версии.
Test Explorer теперь обеспечивает лучшую обработку больших наборов тестов, более простую фильтрацию, закладки плейлистов тестов и настраиваемые столбцы для точной настройки отображаемой тестовой информации.
Версия 16.2 поддерживает отладку JavaScript в новом браузере Microsoft Edge Insider для проектов ASP.NET и ASP.NET Core. Для этого установите браузер, установите точку останова в JavaScript приложения и запустите сеанс отладки.
Кроме того вернули пункт меню Sort Usings, появилась возможность преобразовывать операторы switch в выражения switch, а также сгенерировать параметр для переменной из меню Quick Actions.

Источник: https://devblogs.microsoft.com/visualstudio/
День сто семьдесят девятый. #юмор
Бывало такое? )))
День сто восьмидесятый. #ЗаметкиНаПолях
Многопоточность. Начало
Многопоточность позволяет увеличивать скорость реагирования приложения и, если приложение работает в многопроцессорной или многоядерной системе, скорость его исполнения.
Процесс — это набор ресурсов, используемый отдельным экземпляром приложения. Операционная система использует процессы для разделения исполняемых приложений. Поток — это основная единица, которой операционная система выделяет время процессора.

Когда используется многопоточность
Несколько потоков используются, чтобы повысить скорость отклика вашего приложения и использовать преимущества многопроцессорной или многоядерной системы для увеличения скорости приложения.
1. В приложении основной поток отвечает за пользовательский интерфейс и отвечает на действия пользователя. Рабочие потоки используются для выполнения ресурсоёмких операций, которые в противном случае заняли бы основной поток и сделали бы пользовательский интерфейс невосприимчивым. 2. Выделенный поток может использоваться для получения данных по сети или от внешнего устройства.
3. Если ваша программа выполняет операции, которые могут исполняться параллельно, общее время выполнения можно уменьшить, выполнив эти операции в отдельных потоках и запустив программу в многопроцессорной или многоядерной системе. В такой системе использование многопоточности может ускорить вычисления наряду с повышением отзывчивости приложения.
4. Потоки позволяют изолировать один код от другого, повышая надёжность приложения. В отдельном потоке можно запускать сторонний код, если нет уверенности в его надёжности и качестве.
5. Иногда код проще писать, если предположить, что он будет исполняться в собственном потоке. Однако при этом потребуются дополнительные ресурсы и, возможно, код синхронизации, что снизит эффективность кода.

Приоритеты потоков
Операционные системы с многозадачностью должны использовать некий алгоритм, определяющий порядок и продолжительность исполнения потоков. Windows называют многопоточной ОС с вытесняющей многозадачностью, т.к. каждый поток может быть остановлен в произвольный момент и вместо него выбран для исполнения другой. Чтобы управлять этим процессом, каждому потоку назначается уровень приоритета от 0 (самого низкого) до 31 (самого высокого). При выборе потока, который будет передан процессору, сначала рассматриваются потоки с самым высоким приоритетом. В Windows определены приоритеты процессов (приложений): Idle, Below Normal, Normal (по умолчанию), Above Normal, High и Realtime. Кроме того, поддерживаются следующие относительные приоритеты потоков: Idle, Lowest, Below Normal, Normal, Above Normal, Highest, Time-Critical. Соотношение между приоритетом процесса и относительным приоритетом потока и определяет итоговый уровень приоритета процесса. Например, по умолчанию оба приоритета установлены в Normal, и итоговый приоритет процесса – 8. Потоки с относительным приоритетом Time-Critical при любом приоритете процесса, кроме Realtime, имеют приоритет 15. В Realtime процессе потоки имеют приоритеты от 16 до 31.

Фоновые и активные потоки
Фоновые потоки приложения завершаются немедленно и без появления исключений при завершении активных потоков. Таким образом активные потоки используются для исполнения заданий, которые обязательно нужно завершить. Фоновые потоки можно оставить для некритичных операций. Потоки можно превращать из активного в фоновый и обратно. Основной поток приложения и все потоки, явно созданные через создание объекта Thread, являются активными. Потоки из пула потоков (ThreadPool) по умолчанию фоновые.
Особенности потоков
- В настоящее время потоки в CLR аналогичны Windows-потокам, но не исключено, что со временем эти понятия начнут различаться. Может появиться собственная концепция логического потока, не совпадающая с физическим потоком Windows.
- Для каждого потока выделяются память для хранения информации о потоке, кэша потока, пользовательского стека и т.п.
- В произвольный момент времени Windows передаёт процессору на исполнение один поток. Этот поток исполняется в течение некоторого временного интервала, называемого тактом. После завершение такта происходит переключение на другой поток. Это называется переключением контекста.
- Из-за затрат памяти и времени на переключение контекста, затрат на создание, управление и завершение потока, затрат на сборку мусора и т.п. использования потоков нужно по возможности избегать. Лучше прибегать к доступному в CLR пулу потоков (об этом далее).

Продолжение следует…

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 25.
-
https://docs.microsoft.com/dotnet/standard/threading/threads-and-threading
День сто восемьдесят первый. #ЗаметкиНаПолях
Многопоточность.
2. Пул потоков в CLR
CLR способна управлять собственным пулом потоков. Для каждого процесса существует свой пул, используемый всеми доменами приложений в CLR. Пул потоков позволяет найти золотую середину в ситуации, когда малое количество потоков экономит ресурсы, а большое позволяет воспользоваться преимуществами многопроцессорных систем, а также многоядерных и гиперпотоковых процессоров. Он действует по эвристическому алгоритму, создавая или уничтожая потоки по мере необходимости.
В пуле различают два типа потоков: рабочие потоки (worker thread) и потоки ввода-вывода (I/O thread). Первые используются для асинхронных вычислительных операций, вторые служат для уведомления кода о завершении асинхронной операции ввода-вывода.
Для добавления в очередь пула потоков асинхронных вычислительных операций обычно вызывают один из методов QueueUserWorkItem класса ThreadPool. В метод передаётся делегат WaitCallback, а также может передаваться объект состояния state:
static void Main(string[] args)
{
Console.WriteLine("Основной поток: вызываем асинхронную операцию");
ThreadPool.QueueUserWorkItem(Compute, 5);
Console.WriteLine("Основной поток: выполняем другую работу...");
Thread.Sleep(10000);
Console.ReadLine();
}

private static void Compute(object state)
{
//Метод выполняется потоком из пула
Console.WriteLine($"В Compute: state={state}");
Thread.Sleep(1000);
}
После возвращения управления асинхронным методом, поток возвращается в пул и ожидает следующего задания.

Возможны ситуации, когда требуется явно создать поток, исполняющий конкретную вычислительную операцию:
- Поток требуется запустить с нестандартным приоритетом (хотя, этого делать не рекомендуется).
- Чтобы приложение не закрылось до завершения потоком задания, требуется, чтобы поток исполнялся в активном режиме. В примере выше по умолчанию метод Compute выполняется в фоновом потоке, поэтому приложение может прекратить работу перед тем, как завершится выполнение этого метода.
- Задания, связанные с критически важными вычислениями, которые могут выполняться долго, лучше не отдавать на откуп пула потоков, а выделить им отдельный поток.
- Если есть необходимость преждевременно завершить исполняющийся поток через Thread.Abort().

Продолжение следует…

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 26.
👍1
День сто восемьдесят второй. #ЗаметкиНаПолях
Многопоточность
3. Скоординированная отмена
.NET предлагает стандартный шаблон операций отмены. Он называется скоординированным (cooperative), то есть необходима явная поддержка отмены операций. Это значит, что как код, выполняющий отменяемую операцию, так и код, пытающийся отменить операцию, должны использовать одинаковые типы.
В вызывающем коде создаётся объект CancellationTokenSource. C его помощью можно создать структуру CancellationToken, которая передаётся вызываемому коду. Для отмены операции вызывается метод Cancel объекта CancellationTokenSource.
Вот наиболее полезные члены структуры CancellationToken:
- bool IsCancellationRequested – вызываемый код может периодически обращаться к этому свойству, чтобы узнать, не запрошена ли отмена операции;
- void ThrowIfCancellationRequested() – метод используется в заданиях (Task) аналогично предыдущему свойству;
- CancellationToken None – статическое свойство, обозначающее отсутствие токена отмены, чтобы исключить возможность отмены операции;
- CancellationTokenRegistration Register(…) – метод используется для регистрации одного или нескольких делегатов обратного вызова, которые будут вызваны при отмене операции. Возвращаемая методом структура CancellationTokenRegistration содержит метод Dispose, который позволяет отменить регистрацию.

В примере ниже токен отмены передаётся методу Compute, внутри которого периодически проверяется свойство IsCancellationRequested. Когда пользователь нажимает Enter, вызывается отмена операции, и на следующей итерации цикла он прерывается:
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(o => Compute(cts.Token, 1000));
Console.WriteLine("Нажмите <Enter> для отмены...");
Console.ReadLine();
cts.Cancel();
Console.ReadLine();
}

private static void Compute(CancellationToken token, int countTo)
{
for (int i = 0; i < countTo; i++)
{
if(token.IsCancellationRequested)
{
Console.WriteLine("Счёт отменён");
break;
}
Console.WriteLine(i);
Thread.Sleep(200);
}
Console.WriteLine("Счёт закончен");
}

Продолжение следует…

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 26.
День сто восемьдесят третий. #юмор
18+
День сто восемьдесят четвёртый. #ЗаметкиНаПолях
Многопоточность.
4. Задания. Начало
Проблема с асинхронными вычислениями, запущенными с помощью метода ThreadPool.QueueUserWorkItem() состоит в том, что отсутствует встроенный механизм, позволяющий узнать о завершении операции и получить возвращаемое значение. Для решения этой проблемы используются задания. Следующие вызовы асинхронных операций аналогичны:
ThreadPool.QueueUserWorkItem(Compute, 5);
new Task(Compute, 5).Start();
Объекту Task передаётся делегат Action или Action<object> (в последнем случае следует также передать аргумент метода, как в примере выше). При желании также можно передать структуру CancellationToken для отмены задания. Допустим, метод Calc принимает целое число и производит вычисления. Тогда запустить его в новом задании можно следующим образом:
var t = new Task<int>((n) => Calc((int)n), 10000);
t.Start();
t.Wait();
Console.WriteLine($"Результат: " + t.Result);
Console.ReadLine();
Поток запускает задание и дожидается его выполнения при вызове метода Wait или свойства Result. При этом само задание может выполниться как в новом потоке (тогда поток, вызвавший метод Wait, блокируется), либо в том же потоке.

Отмена задания
Для отмены задания можно воспользоваться объектом CancellationTokenSource (см. https://t.me/NetDeveloperDiary/219 ). Однако, в отличие от обычной асинхронной операции, для отмены задания нужно обратиться не к свойству IsCancellationRequested, а вызывать метод ThrowIfCancellationRequested() токена отмены. Это приводит к исключению OperationCancelledException, которое нужно перехватить в вызывающем коде. Причина в том, что задания возвращают результат. Поэтому, чтобы отличить законченное задание от незаконченного, используется исключение.
private static int Calc(CancellationToken ct, int n)
{

ct.ThrowIfCancellationRequested();

}

var cts = new CancellationTokenSource();
var t = new Task<int>(() => Calc(cts.Token, 10000), cts.Token);
t.Start();

// вызывается асинхронно
// к этому моменту задание может быть уже выполнено
cts.Cancel();

try
{
Console.WriteLine($"Результат: {t.Result}");
}
catch(AggregateException ex)
{
ex.Handle(x => x is OperationCanceledException);
Console.WriteLine("Операция отменена");
}
Все необработанные исключения, возникающие в задании, проглатываются в исполняющем потоке и сохраняются в коллекции InnerExceptions объекта AggregateException. Поэтому в вызывающем коде перехватывается именно исключение типа AggregateException. Далее с помощью метода Handle() можно отметить нужные нам исключения, как обработанные. В данном случае мы считаем все исключения типа OperationCanceledException обработанными. Если в коллекции после вызова метода Handle останутся необработанные исключения, они попадут в новый объект AggregateException.

Продолжение следует…

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 26.
День сто восемьдесят пятый. #ЗаметкиНаПолях
Многопоточность.
5. Задания. Продолжение
Автоматический запуск задания по завершении предыдущего
Вызов метода Wait или свойства Result при незавершённом задании скорее всего приведёт к блокировке текущего потока и появлению в пуле нового потока. Чтобы этого не происходило, можно переписать код из предыдущих примеров так, чтобы результат выводился в новом задании, которое стартует после завершения предыдущего:
var t = new Task<int>((n) => Calc((int)n), 10000);
t.Start();
Task cwt = t.ContinueWith(task => Console.WriteLine($"Результат: {task.Result}"));
При этом поток, исполняющий этот код и ожидающий завершения каждого из этих заданий, не блокируется, а выполняет другую работу или возвращается в пул. Поскольку метод ContinueWith также возвращает Task, то и к нему, в свою очередь, можно применять Wait, Result или ContinueWith.
Кроме того, вызвать ContinueWith можно с флагами TaskContinuationOptions. Наиболее интересные из них:
- AttachedToParent – присоединяет задание к родительскому (родительское задание не закончится, пока не исполнятся все дочерние)
- ExecuteSynchronously – новое задание будет выполнено тем же потоком, что и предыдущее
- OnlyOnCanceled – выполнять, если предыдущее задание отменено
- OnlyOnFaulted – выполнять, если предыдущее задание привело к ошибке
- OnlyOnRanToCompletion – выполнять, если предыдущее задание завершилось успешно
- NotOnCanceled, NotOnFaulted, NotOnRanToCompletion – соответствующие отрицательные флаги.
Вывод результата в предыдущем примере можно переписать с этими флагами:
t.ContinueWith(task => 
Console.WriteLine($"Результат: {task.Result}"),
TaskContinuationOptions.OnlyOnRanToCompletion);
t.ContinueWith(task =>
Console.WriteLine($"Ошибка: {task.Exception}"),
TaskContinuationOptions.OnlyOnFaulted);
t.ContinueWith(task =>
Console.WriteLine("Операция отменена"),
TaskContinuationOptions.OnlyOnCanceled);

Фабрики заданий
Если необходим набор заданий в одном состоянии и с одинаковыми параметрами, можно создать фабрику заданий TaskFactory:
string[] files = null
string[] dirs = null;
string docsDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

var tf = new TaskFactory();
var tasks = new[]
{
tf.StartNew(() => files = Directory.GetFiles(docsDir)),
tf.StartNew(() => dirs = Directory.GetDirectories(docsDir))
};
tf.ContinueWhenAll(tasks, completed => {
Console.WriteLine("{0} содержит: ", docsDir);
Console.WriteLine(" {0} папок", dirs.Length);
Console.WriteLine(" {0} файлов", files.Length);
});
В коде выше создаётся новая фабрика заданий. Ей можно передать общий токен отмены, параметры создания и продолжения заданий и т.п. Далее задания создаются вызовом метода StartNew. Впоследствии можно добавить задание-продолжение по окончании всех или любого из заданий, вызвав соответственно ContinueWhenAll или ContinueWhenAny. В примере выше в фабрике заданий два параллельных задания подсчитывают количество папок и файлов в папке «Мои документы» текущего пользователя, а после завершения всех заданий результаты выводятся в консоль.

Продолжение следует…

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 26.
-
https://docs.microsoft.com/dotnet/api/system.threading.tasks.task.factory
День сто восемьдесят шестой. #юмор
День сто восемьдесят седьмой. #ЗаметкиНаПолях
Многопоточность.
6. Класс Parallel
Для реализации наиболее популярных сценариев посредством заданий создан класс System.Threading.Tasks.Parallel. Например, следующий код вызывает метод DoWork последовательно:
for(int i = 0; i < 1000; i++) DoWork(i);
Вместо этого можно распределить обработку между несколькими потоками пула:
Parallel.For(0, 1000, i => DoWork(i));

Аналогично метод foreach
foreach(var item in collection) DoWork(item);
можно заменить
Parallel.ForEach(collection, item => DoWork(item));

Параллельно запустить несколько методов можно с помощью метода Invoke:
Parallel.Invoke(
() => Method1(),
() => Method2(),
() => Method3());

Особенности:
1. Вызывающий поток также принимает участие в обработке и не блокируется, за исключением случаев, когда он заканчивает выполнение работы, а остальные потоки ещё нет.
2. Если какая-либо операция выбрасывает необработанное исключение, то вызванный вами метод класса Parallel выбросит исключение AggregateException (см. https://t.me/NetDeveloperDiary/221)
3. Использовать методы класса Parallel выгодно, лишь когда задания действительно могут выполняться параллельно, а также не обращаются к общим ресурсам.

Опции
Методам класса Parallel можно передать объект ParallelOptions со следующими свойствами:
- CancellationToken (токен отмены операции, по умолчанию CancellationToken.None)
- MaxDegreeOfParallelizm (максимальное количество рабочих элементов, по умолчанию -1 равно числу доступных процессоров)
- TaskScheduler – планировщик заданий.

Продолжение следует…

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 26.
День сто восемьдесят восьмой.
C# In Depth
Еще полгода назад писал про эту книгу Джона Скита (Jon Skeet) и только недавно до неё «дошли руки». Ну, что сказать. Если Рихтер – мастрид для C# мидл-сениор разработчика, то Скит – это абсолютный мастрид после Рихтера. Признаюсь честно, я Рихтера читал дважды. После первого прочтения не осталось в голове почти ничего (практики не было тогда). А после второго в принципе бОльшая часть контента более-менее запомнилась, но осталась парочка серьёзных проблем. Во-первых, всё равно в голове получилась некоторая каша, и многие понятия остались существовать в каком-то вакууме. Не знаю, проблема ли в организации книги, либо сугубо моего восприятия, но факт остаётся фактом. Во-вторых, и это, наверное, главная проблема. Моё издание книги вышло в 2012 году. И даже последнее на данный момент издание 2013 года и охватывает C# 5. А сейчас уже выходит 8я(!!!) версия языка. Таким образом, несмотря на то, что книга невероятно полезна с теоретической точки зрения, она «страшно далека от реальности». Поэтому получается, что в книге есть главы на десятки страниц, например, про делегаты или асинхронные операции, но очень коротко описано то, как они действительно используются сейчас (лямбда-выражения, LINQ или async/await). То есть в знаниях получается «дыра» между хорошей теорией 6-летней давности и современными тенденциями разработки. Это не значит, что описанная теория совсем бесполезна. Но получается что-то вроде того, как один из наших преподавателей в вузе считал, что изучать программирование надо с ассемблера (Ну... как бы, да... но можно и нет :) )
И в этом смысле прочитать Скита именно ПОСЛЕ Рихтера, на мой взгляд, крайне полезно. В моём случае книга Скита – это 4е издание 2019 года, которое описывает самые последние изменения в языке (вплоть до краткого обзора 8й версии). Но самое главное, что все инструменты языка рассмотрены в хронологическом порядке: от первой версии до седьмой. Про каждый инструмент подробно описано: что это, когда возникло, почему возникло, что изменилось от версии к версии и где это применяется. Это позволяет расставить все ваши знания по полочкам и связать различные знания между собой. К примеру, большинство изменений в C#3 (неявное объявление переменных через var, инициализаторы объектов и коллекций, анонимные типы, методы расширения и т.п.) введены для того, чтобы использовать лямбда-выражения и LINQ. И вся книга построена по этому принципу.
Кроме того, в отличие от Рихтера, Скит пишет в очень простой, понятной и дружелюбной манере, поэтому книгу, даже по-английски, читаешь не через силу (потому, что надо), а с удовольствием, как набор постов в блоге.
Далее на канале периодически буду приводить выдержки из книги, которые показались наиболее интересными.

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012.
- Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019.
День сто восемьдесят девятый. #ЗаметкиНаПолях
Многопоточность
7. Шаблоны Асинхронного Программирования
.NET предоставляет три шаблона для выполнения асинхронных операций:
1. Модель асинхронного программирования (APM), также называемый шаблоном IAsyncResult, является устаревшей моделью, использующей интерфейс IAsyncResult для обеспечения асинхронного поведения. В этом шаблоне для синхронных операций требуются методы Begin и End (например, BeginWrite и EndWrite для реализации асинхронной операции записи). Этот шаблон больше не рекомендуется для новых разработок. Например, рассмотрим метод Read, который считывает указанное количество данных в буфер, начиная с указанного смещения:
public class MyClass
{
public int Read(byte [] buffer, int offset, int count);
}
В APM нужно реализовать методы BeginRead и EndRead:
public class MyClass
{
public IAsyncResult BeginRead(
byte[] buffer, int offset, int count,
AsyncCallback callback, object state);
public int EndRead(IAsyncResult asyncResult);
}

2. Асинхронный шаблон на основе событий (EAP) является устаревшим шаблоном на основе событий для обеспечения асинхронного поведения. Для этого требуется метод с суффиксом Async и одно или несколько событий, делегатов обработчиков событий и типов, производных от EventArg. EAP был представлен в .NET Framework 2.0. Больше не рекомендуется для новых разработок. Для примера выше в EAP потребуется метод и событие:
public class MyClass
{
public void ReadAsync(byte[] buffer, int offset, int count);
public event ReadCompletedEventHandler ReadCompleted;
}

3. Асинхронный шаблон на основе задач (TAP) использует общий метод для инициирования и завершения асинхронной операции. TAP был представлен в .NET Framework 4. В настоящее время это рекомендуемый подход к асинхронному программированию в .NET. Языковую поддержку для TAP обеспечивают ключевые слова async и await в C#. Для примера выше в TAP потребуется реализация одного метода:
public class MyClass
{
public Task<int> ReadAsync(byte[] buffer, int offset, int count);
}

Продолжение следует…

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 27.
-
https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/
День сто девяностый. #ЧтоНовенького
Предпросмотр Инструментов в Visual Studio
Обновлено окно Предпросмотр Инструментов (Preview Features) в разделе Инструменты > Параметры > Среда (Tools > Options > Environment). Новый вид предоставляет больше информации и возможность оставить отзыв о новых функциях. Пока они находятся в разработке, вы можете отключить любую из них, если столкнетесь с проблемами. Microsoft также рекомендует оставлять отзывы о любых проблемах, с которыми вы столкнётесь. Список на этой странице включают функции на ранней стадии разработки, которые влияют на существующую функциональность, те, которые еще дорабатываются, а также экспериментальные инструменты.
Поскольку разработка нового функционала идёт постоянно, список в окне Предпросмотр Инструментов будет изменяться в каждом выпуске Visual Studio, так как некоторые функции добавляются в продукт, а другие удаляются.

Источник: https://devblogs.microsoft.com/visualstudio/preview-features-in-visual-studio/