Проверяем заголовок PNG и читаем размеры!
Нужно быстро узнать размеры изображения до декодирования всего файла. В этой задаче читаем фиксированные 24 байта, проверяем сигнатуру PNG и первый чанк IHDR, затем получаем ширину и высоту.
В решении:
Такой код подходит для предварительной проверки загрузок и очередей обработки изображений.
➡️ C# Ready | #задача
Нужно быстро узнать размеры изображения до декодирования всего файла. В этой задаче читаем фиксированные 24 байта, проверяем сигнатуру PNG и первый чанк IHDR, затем получаем ширину и высоту.
В решении:
• читаем заголовок без загрузки изображения целиком
• отклоняем усечённый или неверный файл
• декодируем размеры с учётом порядка байтов
Такой код подходит для предварительной проверки загрузок и очередей обработки изображений.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤4🔥4
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥5👍3🤝2
Знали, почему долгоживущий HttpClient может обращаться по устаревшему DNS-адресу?
HttpClient повторно использует соединения из пула, а DNS обычно проверяется при установке нового соединения. Если адрес сервиса изменился, уже открытое соединение не обязано сразу переключиться на новый IP.
Создаём общий клиент и задаём срок жизни соединений:
После истечения интервала старое соединение перестаёт использоваться для новых запросов, а следующее подключение заново разрешает имя хоста. Значение выбираем по ожидаемой частоте DNS-изменений и особенностям инфраструктуры.
Клиент повторно используем между запросами:
Создание нового HttpClient на каждый запрос может постоянно плодить пулы соединений и привести к исчерпанию сокетов. Для приложений с DI вместо статического клиента подходит IHttpClientFactory с настройкой handler lifetime.
➡️ C# Ready | #совет
HttpClient повторно использует соединения из пула, а DNS обычно проверяется при установке нового соединения. Если адрес сервиса изменился, уже открытое соединение не обязано сразу переключиться на новый IP.
Создаём общий клиент и задаём срок жизни соединений:
static readonly HttpClient Client = new(
new SocketsHttpHandler
{
PooledConnectionLifetime =
TimeSpan.FromMinutes(5)
});
После истечения интервала старое соединение перестаёт использоваться для новых запросов, а следующее подключение заново разрешает имя хоста. Значение выбираем по ожидаемой частоте DNS-изменений и особенностям инфраструктуры.
Клиент повторно используем между запросами:
using var response =
await Client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
Создание нового HttpClient на каждый запрос может постоянно плодить пулы соединений и привести к исчерпанию сокетов. Для приложений с DI вместо статического клиента подходит IHttpClientFactory с настройкой handler lifetime.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3🔥3
Как индексация строк изменила систему типов HydraScript!
Автор добавил длину и обращение по индексу в интерпретируемый язык на C#. Работа над строками показала, что правила операций удобнее хранить рядом с типами, а не разносить по семантическому анализатору.
В статье разбирают:
• как интерфейс IOperator описывает допустимость операции и тип результата
• почему строки и массивы могут использовать общие правила индексации
• где проходит граница между проверкой типов и выполнением выражения
Такой подход упрощает развитие анализатора, но оставляет диагностику ошибок и контекст исходного кода в соответствующем слое.
➡️ C# Ready | #статья
Автор добавил длину и обращение по индексу в интерпретируемый язык на C#. Работа над строками показала, что правила операций удобнее хранить рядом с типами, а не разносить по семантическому анализатору.
В статье разбирают:
• как интерфейс IOperator описывает допустимость операции и тип результата
• почему строки и массивы могут использовать общие правила индексации
• где проходит граница между проверкой типов и выполнением выражения
Такой подход упрощает развитие анализатора, но оставляет диагностику ошибок и контекст исходного кода в соответствующем слое.
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥4👍3
Запускаем периодическую проверку без наложения запросов!
Обычный таймер может вызвать следующий callback, пока предыдущая проверка ещё ждёт сеть. Сегодня соберём последовательный polling через PeriodicTimer и передадим отмену внутрь операции.
Создаём таймер с нужным интервалом:
PeriodicTimer допускает одного ожидающего потребителя. Если очередная проверка длится дольше интервала, накопившиеся тики не создают параллельные копии запроса.
Ждём тик и завершаем цикл через cancellation token:
Следующая итерация начнётся только после завершения RefreshAsync. Поэтому медленный ответ увеличит фактический интервал, но проверки не наложатся друг на друга.
Внутри проверки передаём тот же token сетевому вызову:
При остановке фоновой службы token прервёт ожидание тика и поддерживающий отмену HTTP-запрос.
➡️ C# Ready | #практика
Обычный таймер может вызвать следующий callback, пока предыдущая проверка ещё ждёт сеть. Сегодня соберём последовательный polling через PeriodicTimer и передадим отмену внутрь операции.
Создаём таймер с нужным интервалом:
using var timer = new PeriodicTimer(
TimeSpan.FromSeconds(15));
PeriodicTimer допускает одного ожидающего потребителя. Если очередная проверка длится дольше интервала, накопившиеся тики не создают параллельные копии запроса.
Ждём тик и завершаем цикл через cancellation token:
while (await timer.WaitForNextTickAsync(token))
{
await RefreshAsync(token);
}
Следующая итерация начнётся только после завершения RefreshAsync. Поэтому медленный ответ увеличит фактический интервал, но проверки не наложатся друг на друга.
Внутри проверки передаём тот же token сетевому вызову:
using var response = await client.GetAsync(
endpoint, token);
response.EnsureSuccessStatusCode();
При остановке фоновой службы token прервёт ожидание тика и поддерживающий отмену HTTP-запрос.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥4
Как код C# превращается в машинные инструкции!
Схема показывает весь путь от исходного файла до выполнения процессором. Компилятор C# сначала создаёт промежуточный IL и сборку, а не готовые инструкции для конкретного процессора.
При запуске CLR загружает сборку. Когда вызывается метод, JIT-компилятор переводит его IL в машинный код для текущей платформы.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
Схема показывает весь путь от исходного файла до выполнения процессором. Компилятор C# сначала создаёт промежуточный IL и сборку, а не готовые инструкции для конкретного процессора.
При запуске CLR загружает сборку. Когда вызывается метод, JIT-компилятор переводит его IL в машинный код для текущей платформы.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥3
Знаете, почему регулярное выражение может надолго занять поток?
При неудачном совпадении шаблоны с вложенными повторами могут проверять множество вариантов. На пользовательском вводе такая обратная проверка способна надолго загрузить процессор:
По умолчанию Regex может работать без ограничения времени. Для обычного движка задаём конечный timeout при создании объекта:
Если поиск не уложится в интервал, будет выброшено RegexMatchTimeoutException. Обрабатываем её отдельно и возвращаем понятную ошибку проверки:
В .NET 7 и новее можно рассмотреть RegexOptions.NonBacktracking, если шаблон не использует неподдерживаемые конструкции вроде lookaround или backreference.
➡️ C# Ready | #совет
При неудачном совпадении шаблоны с вложенными повторами могут проверять множество вариантов. На пользовательском вводе такая обратная проверка способна надолго загрузить процессор:
var pattern = "^(a+)+$";
var regex = new Regex(pattern);
По умолчанию Regex может работать без ограничения времени. Для обычного движка задаём конечный timeout при создании объекта:
var regex = new Regex(pattern, RegexOptions.None,
TimeSpan.FromMilliseconds(100));
Если поиск не уложится в интервал, будет выброшено RegexMatchTimeoutException. Обрабатываем её отдельно и возвращаем понятную ошибку проверки:
try {
return regex.IsMatch(input);
} catch (RegexMatchTimeoutException) {
throw new ValidationException(
"Regex validation timed out");
}В .NET 7 и новее можно рассмотреть RegexOptions.NonBacktracking, если шаблон не использует неподдерживаемые конструкции вроде lookaround или backreference.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4🔥3
Добавляем замер времени HTTP-запросов через DelegatingHandler!
Когда несколько сервисов обращаются к внешним API, можно видеть статус и длительность каждого запроса. Вынесем замер в обработчик HttpClient, чтобы не дублировать Stopwatch в каждом месте вызова.
Создаём обработчик, который получает логгер через конструктор:
DelegatingHandler передаёт запрос следующему обработчику цепочки. Параметр primary constructor доступен методам класса, поэтому отдельное поле для log не требуется.
Переопределяем асинхронную отправку и сохраняем контракт базового класса:
Токен отмены передаём дальше без замены, чтобы отмена вызывающего кода дошла до сетевого запроса.
Перед отправкой запускаем секундомер и готовим поле статуса:
Переменная status остаётся null, если транспорт завершился исключением до получения ответа. Это позволяет логировать и успешные ответы, и неудачные попытки соединения.
Выполняем запрос через следующий обработчик и запоминаем статус:
Ответ возвращается вызывающему коду без чтения или изменения его тела. Блок finally выполняется и после ответа, и при сетевом исключении.
В finally записываем метод, статус и длительность:
Регистрируем обработчик как transient и добавляем его в именованный клиент:
Фабрика создаёт цепочки обработчиков для клиентов, поэтому один экземпляр DelegatingHandler нельзя переиспользовать в нескольких цепочках.
➡️ C# Ready | #практика
Когда несколько сервисов обращаются к внешним API, можно видеть статус и длительность каждого запроса. Вынесем замер в обработчик HttpClient, чтобы не дублировать Stopwatch в каждом месте вызова.
Создаём обработчик, который получает логгер через конструктор:
sealed class TimingHandler(
ILogger<TimingHandler> log) :
DelegatingHandler
{
}
DelegatingHandler передаёт запрос следующему обработчику цепочки. Параметр primary constructor доступен методам класса, поэтому отдельное поле для log не требуется.
Переопределяем асинхронную отправку и сохраняем контракт базового класса:
protected override
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken token)
Токен отмены передаём дальше без замены, чтобы отмена вызывающего кода дошла до сетевого запроса.
Перед отправкой запускаем секундомер и готовим поле статуса:
var timer = Stopwatch.StartNew();
HttpStatusCode? status = null;
Переменная status остаётся null, если транспорт завершился исключением до получения ответа. Это позволяет логировать и успешные ответы, и неудачные попытки соединения.
Выполняем запрос через следующий обработчик и запоминаем статус:
try
{
var response = await base.SendAsync(
request, token);
status = response.StatusCode;
return response;
}
Ответ возвращается вызывающему коду без чтения или изменения его тела. Блок finally выполняется и после ответа, и при сетевом исключении.
В finally записываем метод, статус и длительность:
finally
{
log.LogInformation(
"{Method} {Status} {Ms}ms",
request.Method, status,
timer.ElapsedMilliseconds);
}
Регистрируем обработчик как transient и добавляем его в именованный клиент:
builder.Services.AddTransient<TimingHandler>();
builder.Services.AddHttpClient("catalog")
.AddHttpMessageHandler<TimingHandler>();
Фабрика создаёт цепочки обработчиков для клиентов, поэтому один экземпляр DelegatingHandler нельзя переиспользовать в нескольких цепочках.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4🔥3
Почему Task.Run не делает синхронный ввод-вывод асинхронным?
Оборачивать блокирующее чтение файла в Task.Run кажется способом освободить текущий поток, но ожидание всё равно занимает поток из пула:
Операция остаётся синхронной. Мы лишь переносим блокирующее ожидание на другой поток, поэтому под нагрузкой такие обёртки могут быстрее исчерпать пул.
Для чтения файла используем API с настоящей асинхронной операцией:
Пока система ждёт данные, рабочий поток не занят синхронным чтением. Переданный токен позволяет отменить операцию, если результат больше не нужен.
Токен отмены передаём и в сам метод чтения, и дальше по цепочке:
Для CPU-вычислений Task.Run может быть уместен, например чтобы не блокировать UI-поток:
Синхронная функция должна сама проверять отмену, потому что токен не прерывает уже запущенный код:
Независимые операции с асинхронным API запускаем напрямую и ожидаем вместе:
Так ожидание I/O не расходует потоки на блокировку. В серверном коде Task.Run вокруг обычного ввода-вывода чаще добавляет переключение потоков, а не пропускную способность.
➡️ C# Ready | #совет
Оборачивать блокирующее чтение файла в Task.Run кажется способом освободить текущий поток, но ожидание всё равно занимает поток из пула:
await Task.Run(() => File.ReadAllText(path));
Операция остаётся синхронной. Мы лишь переносим блокирующее ожидание на другой поток, поэтому под нагрузкой такие обёртки могут быстрее исчерпать пул.
Для чтения файла используем API с настоящей асинхронной операцией:
string text = await File.ReadAllTextAsync(
path, cancellationToken);
Пока система ждёт данные, рабочий поток не занят синхронным чтением. Переданный токен позволяет отменить операцию, если результат больше не нужен.
Токен отмены передаём и в сам метод чтения, и дальше по цепочке:
await using var stream = File.OpenRead(path);
using var reader = new StreamReader(stream);
string text = await reader.ReadToEndAsync(token);
Для CPU-вычислений Task.Run может быть уместен, например чтобы не блокировать UI-поток:
var result = await Task.Run(
() => Calculate(input, token), token);
Синхронная функция должна сама проверять отмену, потому что токен не прерывает уже запущенный код:
foreach (var item in items)
{
token.ThrowIfCancellationRequested();
Process(item);
}
Независимые операции с асинхронным API запускаем напрямую и ожидаем вместе:
Task<string> config = File.ReadAllTextAsync(
configPath);
Task<string> data = File.ReadAllTextAsync(
dataPath);
await Task.WhenAll(config, data);
Так ожидание I/O не расходует потоки на блокировку. В серверном коде Task.Run вокруг обычного ввода-вывода чаще добавляет переключение потоков, а не пропускную способность.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥2
Строим порядок сборки из зависимостей!
Есть список задач и связи между ними. Нужно определить, в каком порядке их можно выполнять, а при циклической зависимости вернуть понятную ошибку.
В этой задаче:
Алгоритм Кана проходит каждую задачу и каждую связь один раз.
➡️ C# Ready | #задача
Есть список задач и связи между ними. Нужно определить, в каком порядке их можно выполнять, а при циклической зависимости вернуть понятную ошибку.
В этой задаче:
• строим граф зависимостей и считаем входящие связи
• обрабатываем готовые задачи через очередь
• проверяем, что в графе не осталось цикла
Алгоритм Кана проходит каждую задачу и каждую связь один раз.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3🔥3