В C# потоки управляются через класс
Thread из пространства имен System.Threading. Поток проходит несколько этапов на протяжении своего жизненного цикла. Давайте разберём эти этапы.— Unstarted. Не запущен
Поток создан, но ещё не запущен. Он находится в этом состоянии сразу после инициализации объекта
Thread, но до вызова метода Start().Thread thread = new Thread(MyMethod);
// Поток создан, но не запущен
— Running. Выполняется
Поток начинает выполнение после вызова
Start(). В этом состоянии поток выполняет код, переданный в качестве делегата.thread.Start();
// Поток запущен и выполняется
— Waiting. Ожидание
Поток приостанавливается, ожидая выполнения какого-либо условия или ресурса. Это может быть вызвано методами:
•
Thread.Sleep() — поток засыпает на заданное время.•
Monitor.Wait() или lock — поток ожидает захвата монитора.•
Thread.Join() — поток ожидает завершения другого потока.Thread.Sleep(1000);
// Поток приостановлен на 1 секунду
— Blocked. Заблокирован
Поток заблокирован, пытаясь получить доступ к ресурсу, уже занятому другим потоком. Например, ожидание захвата блокировки через
lock.— Stopped. Остановлен
Поток завершает выполнение. Это состояние наступает, когда метод, выполняемый в потоке, завершает работу или вызывается устаревший метод
Abort(). Поток в этом состоянии больше нельзя запустить снова.thread.Join();
// Основной поток ожидает завершения
Дополнительные состояния
— Background. Фоновый поток
Поток может быть фоновым, если свойство
IsBackground установлено в true. Фоновые потоки завершаются автоматически, когда завершается основной поток приложения.thread.IsBackground = true;
// Устанавливаем поток как фоновый
— Suspended. Приостановлен (устарело)
Метод
Suspend() приостанавливал выполнение потока, но был удалён из новых версий .NET из-за возможных проблем с безопасностью.📍 Навигация: Вакансии • Задачи • Собесы
#лучшее_из_библиотеки_2025
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍5
⚙️ Передаём данные между потоками
В C# есть несколько способов передать данные из одного потока в другой. Выбор зависит от требований к производительности, удобству и безопасности. Несколько распространенных вариантов:
1️⃣ Использование BlockingCollection<T>
Это потокобезопасная коллекция, позволяющая передавать данные от одного потока к другому.
2️⃣ Использование TaskCompletionSource<T>
Когда нужно передать значение между потоками в будущем.
3️⃣ Использование ConcurrentQueue<T>
Если нужно неблокирующее хранилище данных.
4️⃣ Использование Channel<T> (System.Threading.Channels)
Альтернативный подход к BlockingCollection<T>
💬 Какой вариант используете в проде?
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#лучшее_из_библиотеки_2025
В C# есть несколько способов передать данные из одного потока в другой. Выбор зависит от требований к производительности, удобству и безопасности. Несколько распространенных вариантов:
Это потокобезопасная коллекция, позволяющая передавать данные от одного потока к другому.
var collection = new BlockingCollection<int>();
// Поток-поставщик (Producer)
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
collection.Add(i);
Console.WriteLine($"Производитель: добавил {i}");
Thread.Sleep(500);
}
collection.CompleteAdding();
});
// Поток-потребитель (Consumer)
Task.Run(() =>
{
foreach (var item in collection.GetConsumingEnumerable())
{
Console.WriteLine($"Потребитель: получил {item}");
}
}).Wait();
Когда нужно передать значение между потоками в будущем.
var tcs = new TaskCompletionSource<int>();
// Поток-поставщик
Task.Run(() =>
{
Thread.Sleep(2000);
tcs.SetResult(42);
});
// Поток-потребитель
int result = await tcs.Task;
Console.WriteLine($"Получено: {result}");
Если нужно неблокирующее хранилище данных.
var queue = new ConcurrentQueue<int>();
// Поток-поставщик
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
queue.Enqueue(i);
Console.WriteLine($"Добавлено {i}");
Thread.Sleep(500);
}
});
// Поток-потребитель
Task.Run(() =>
{
while (true)
{
if (queue.TryDequeue(out int item))
{
Console.WriteLine($"Получено {item}");
}
Thread.Sleep(100);
}
}).Wait();
Альтернативный подход к BlockingCollection<T>
var channel = Channel.CreateUnbounded<int>();
// Поток-поставщик
_ = Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
await channel.Writer.WriteAsync(i);
Console.WriteLine($"Производитель: {i}");
await Task.Delay(500);
}
channel.Writer.Complete();
});
// Поток-потребитель
await foreach (var item in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Потребитель: {item}");
}
📍 Навигация: Вакансии • Задачи • Собесы
#лучшее_из_библиотеки_2025
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩7❤5
Мы раньше кидали эту команду.
dotnet new gitignore делает все за вас. В ней уже прописаны все типичные исключения: каталоги bin/, obj/, кэш NuGet, файлы публикаций, временные артефакты IDE и прочие служебные данные, которые не должны попадать в репозиторий.Не нужно искать шаблон на GitHub или копировать его вручную — всё доступно из коробки.
📍 Навигация: Вакансии • Задачи • Собесы
#лучшее_из_библиотеки_2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43❤2
Каникулы отработали свое, а вместе с ними осталась подборка самых сильных материалов за прошлый год.
Админ врывается в 2026 и вас зовёт за собой. Расскажите чем занимались на праздниках👇
📍 Навигация: Вакансии • Задачи • Собесы
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
В продакшене до сих пор встречается LINQ паттерн, где коллекцию сначала сортируют, а потом берут первый элемент:
var youngest = people
.OrderBy(p => p.Age)
.First();
На вид код нормальный, но по факту он платит за сортировку, хотя нужна всего одна крайняя точка.
Правильнее выразить намерение напрямую через MinBy:
var youngest = people.MinBy(p => p.Age);
MinBy возвращает элемент с минимальным ключом и не требует полной сортировки последовательности. Если в коде встречается OrderBy().First() или OrderByDescending().First(), это повод остановиться и проверить, не ищется ли просто минимум или максимум.📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍33🥱4
Microsoft показала, как делать виджеты для Android-приложений на .NET MAUI. Разбираемся, что к чему.
Виджет — это кусочек вашего приложения, который живёт на домашнем экране телефона. Пользователь видит нужную информацию сразу, без запуска приложения.
Раньше для виджетов приходилось лезть в Java или Kotlin. Теперь можно на C#.
Как устроено
Виджет работает отдельно от приложения. Даже когда приложение закрыто, виджет продолжает показывать данные и реагировать на действия.
Три основных части:
AppWidgetProvider — следит за событиями виджета (создали, обновили, удалили)RemoteViews — рисует интерфейс виджетаBroadcastReceiver — ловит клики и другие действия пользователяВы создаёте класс на C#, прописываете его в манифесте Android, рисуете layout в XML. Стандартная Android-разработка, просто на C#.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
GroupBy в LINQ удобный и выразительный, поэтому его часто тянут в любой код, где надо что то посчитать по ключу.Проблема в том, что
GroupBy решает задачу группировки, а агрегация это частный случай, и за него иногда приходится платить лишним.Типичный паттерн выглядит так:
var totals = orders
.GroupBy(o => o.CustomerId)
.Select(g => new
{
CustomerId = g.Key,
Total = g.Sum(o => o.Amount)
})
.ToList();
Выглядит читабельно, но
GroupBy внутри строит структуру групп, а значит элементы буферизуются и создаются объекты группировок, даже если в итоге нужен только итоговый Total.Если нужна именно аккумуляция, проще и дешевле сделать один проход и складывать суммы в словарь:
var totals = new Dictionary<int, decimal>();
foreach (var order in orders)
{
if (totals.TryGetValue(order.CustomerId, out var current))
totals[order.CustomerId] = current + order.Amount;
else
totals[order.CustomerId] = order.Amount;
}
Это скучно, зато алгоритм читается буквально, один проход, явные обновления, без сюрпризов с материализацией групп.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19
Win8DE воссоздает интерфейс Windows 8 для Wayland. Идеально, если есть ностальгия по fluid-анимациям, но без возврата к устаревшей ОС.
Утилита для обоев, экран блокировки, меню запуска, OSD для громкости и яркости, настройки обоев. Без бесполезного шарм-меню.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱8👍2❤1
Разбираемся, как встроить изображение в PDF так, чтобы всё корректно печаталось, с помощью C#.
IronPdf — это библиотека, которая работает по принципу «что видите в HTML, то получите в PDF». Вместо возни с координатами вы просто верстаете страницу как для браузера.
Библиотека платная для коммерческих проектов, но есть пробный период. Для некоммерческих целей можно использовать бесплатно.
Самый простой способ — сгенерировать HTML с тегом <img> и конвертировать в PDF:
using IronPdf;
var renderer = new ChromePdfRenderer();
string html = @"
<html>
<body>
<img src='logo.png' style='width: 200px;' />
<p>Текст под картинкой</p>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("document_with_image.pdf");
Если картинка приходит из базы данных или веб-формы, удобнее встроить её прямо в HTML через Data URI:
byte[] imageBytes = File.ReadAllBytes("logo.png");
string base64 = Convert.ToBase64String(imageBytes);
string html = $@"
<html>
<body>
<img src='data:image/png;base64,{base64}' style='width: 200px;' />
<h2>Отчёт за декабрь</h2>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");Если нужно добавить штамп или подпись на готовый документ, IronPdf позволяет работать со слоями:
var existingPdf = PdfDocument.FromFile("contract.pdf");
// Создаём HTML со штампом
string stampHtml = @"
<div style='position: absolute; bottom: 50px; right: 50px;'>
<img src='approved_stamp.png' style='width: 100px; opacity: 0.7;' />
</div>";
var stampPdf = renderer.RenderHtmlAsPdf(stampHtml);
// Накладываем на каждую страницу
foreach (var page in existingPdf.Pages)
{
page.AddBackgroundPdf(stampPdf);
}
existingPdf.SaveAs("contract_approved.pdf");Частые ошибки
• Картинка не появляется — проверьте путь к файлу. IronPdf ищет относительно рабочей директории приложения. Используйте Path.GetFullPath() для отладки или встраивайте через Base64.
• Размытые изображения — убедитесь, что исходное разрешение картинки достаточное. Для печати нужно минимум 300 DPI. Если растягиваете маленькое изображение через width в CSS, оно будет мыльным.
• Огромный размер файла — IronPdf встраивает оригиналы. Оптимизируйте картинки заранее: сжимайте JPEG до 80-85% качества, для PNG используйте инструменты вроде TinyPNG.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤3
Одна из самых неприятных просадок производительности в C# начинается не с LINQ операторов, а с того, что коллекции начинают жить как
IEnumerable.Пример который встречается постоянно:
IEnumerable<Order> orders = GetOrders();
if (orders.Count() > 0)
{
// do something
}
Если внутри реально
List или массив, то количество элементов доступно быстро через свойство Count или Length, то есть без прохода по данным.Но
Count() как LINQ метод обязан работать для любого IEnumerable, поэтому в общем случае он перечисляет элементы, пока не посчитает все.Так проверка на пустоту внезапно становится полным обходом, и это особенно больно если orders это запрос с отложенным выполнением или поток данных.
А если хочется сохранить быстрый путь для коллекций, можно явно проверить интерфейс с Count:
if (orders is ICollection<Order> collection)
{
if (collection.Count > 0)
{
// fast path
}
}
else if (orders.Any())
{
// fallback
}
Если в профилировщике внезапно появился лишний проход по данным, стоит проверить где коллекция превратилась в
IEnumerable и где после этого зовется Count().📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱11👍9❤1🤔1😢1
Roadmap для .NET-разработчика: от промптов к Semantic Kernel
ИИ-агенты становятся стандартом автоматизации. Для тех, кто работает в экосистеме
Ваш план развития:
— понимание основ агентной логики и
— проектирование систем памяти и доступа к данным;
— создание мультиагентных пайплайнов для рабочих задач;
— интеграция агентов в бизнес-процессы компании.
Курс «Разработка ИИ-агентов» — это интенсивный путь от теории до создания сложных производственных систем.
Начать погружение в ИИ
До 19 января купите курс и заберите два дополнительных в подарок (акция «3 в 1»).
ИИ-агенты становятся стандартом автоматизации. Для тех, кто работает в экосистеме
Microsoft, это шанс занять нишу архитектора автономных систем.Ваш план развития:
— понимание основ агентной логики и
Reasoning;— проектирование систем памяти и доступа к данным;
— создание мультиагентных пайплайнов для рабочих задач;
— интеграция агентов в бизнес-процессы компании.
Курс «Разработка ИИ-агентов» — это интенсивный путь от теории до создания сложных производственных систем.
Начать погружение в ИИ
До 19 января купите курс и заберите два дополнительных в подарок (акция «3 в 1»).
🥱11👏1🌚1
🌐 Браузер без всего лишнего
Наткнулись на проект, который помогает вырезать из Chrome, Edge и Firefox всё лишнее, что не относится к работе браузера.
Что сносит:
• AI-фичи (Copilot в Edge, AI-саммари в Chrome)
• Телеметрия и отчёты об использовании
• Спонсорский контент и реклама
• Интеграция сторонних продуктов
• Рекомендованные новости и виджеты
• Бесполезные UI-элементы
Вместо ручного копания в about:config или реестре Windows — готовые конфиги + скрипты установки.
➡️ Удалить мусор из браузера
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Наткнулись на проект, который помогает вырезать из Chrome, Edge и Firefox всё лишнее, что не относится к работе браузера.
Что сносит:
• AI-фичи (Copilot в Edge, AI-саммари в Chrome)
• Телеметрия и отчёты об использовании
• Спонсорский контент и реклама
• Интеграция сторонних продуктов
• Рекомендованные новости и виджеты
• Бесполезные UI-элементы
Вместо ручного копания в about:config или реестре Windows — готовые конфиги + скрипты установки.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥7😢2👍1😁1
Проблема LINQ в горячих местах обычно не в вычислениях, а в том, что вокруг каждого запроса появляются маленькие кусочки инфраструктуры.
Where и Select сами по себе ленивые, они не выполняются пока не началось перечисление. Но как только появляется ToArray, начинается реальная работа и выделяется новый массив под результатЕсли внутри цикла постоянно вызывается
ToArray, то постоянно создаются новые массивы.Если еще и лямбды захватывают переменные, добавляются лишние замыкания, и это тоже может стать частью мусора, который потом будет собирать GC.
Пример:
foreach (var batch in batches)
{
var validItems = batch
.Where(IsValid)
.Select(Transform)
.ToArray();
Process(validItems);
}
Ручной цикл выглядит скучнее, но дает контроль над буфером и позволяет переиспользовать память:
var buffer = new List<Result>(batch.Count);
foreach (var item in batch)
{
if (!IsValid(item))
continue;
buffer.Add(Transform(item));
}
Process(CollectionsMarshal.AsSpan(buffer));
buffer.Clear();
CollectionsMarshal.AsSpan дает доступ к внутреннему массиву List как Span, то есть можно передать данные дальше без копирования, но важно не менять List пока Span используется.📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱7👍5👾5❤1
🏭 Новая база корпоративной культуры
В 2025 году индустрия провела эксперимент и пришла к циничному выводу: забота о сотрудниках экономически невыгодна. Теперь карьерный рост зависит не от крутого продукта, а от способности закрывать KPI и не сгореть раньше коллег.
Почему так произошло и кто в этом виноват — разбираемся в статье.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
В 2025 году индустрия провела эксперимент и пришла к циничному выводу: забота о сотрудниках экономически невыгодна. Теперь карьерный рост зависит не от крутого продукта, а от способности закрывать KPI и не сгореть раньше коллег.
Почему так произошло и кто в этом виноват — разбираемся в статье.
📍 Навигация: Вакансии • Задачи • Собесы
Please open Telegram to view this post
VIEW IN TELEGRAM
😢3👏1🤩1🥱1
В дефолтном микшере Windows 11 для настройки громкости отдельных приложений нужно сделать много действий:
1. В меню найти иконку громкости и кликнуть по ней
2. Кликнуть по ещё одной иконке в выпавшем меню
3. Пролистать вниз и настроить громкость.
Мод с Windhawk меняет это: наводим на иконку звука в таскбаре — прокручиваем колёсико мыши — сразу меняем громкость активного приложения.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
SelectMany проектирует каждый элемент во вложенную последовательность и потом склеивает эти последовательности в одну общую.
И это отличный выбор для чистой проекции без сложной логики, когда нужно ровно развернуть коллекцию коллекций.
Но если в задаче появляется управление потоком или условия выхода:
var allItems = orders
.SelectMany(o => o.Items)
.Where(i => i.IsActive)
.ToList();
Цикл часто дает больше ясности и предсказуемости:
var allItems = new List<Item>();
foreach (var order in orders)
{
foreach (var item in order.Items)
{
if (!item.IsActive)
continue;
allItems.Add(item);
}
}
Плюс тут легко вставить
break, continue, счетчики, ограничения, и не гадать как они сложатся с отложенным выполнением.📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔8😁1
«Этот манёвр будет стоить нам 51 год...»
Для
В Proglib Academy поднимаются цены. Успейте забрать курс по старой стоимости:
— Разработка ИИ-агентов
— Математика для разработки AI-моделей
— ML для старта в Data Science
— Математика для Data Science
— Специалист по ИИ
— Алгоритмы и структуры данных
— Программирование на Python
— Основы IT для непрограммистов
— Архитектуры и шаблоны проектирования
Забрать по старой цене
⚠️ Повышение уже 19 января
Для
.NET разработчика архитектура и алгоритмы — это база. Если вы хотите проектировать сложные системы или внедрять ИИ в свои энтерпрайз-решения, сейчас лучший момент для старта.В Proglib Academy поднимаются цены. Успейте забрать курс по старой стоимости:
— Разработка ИИ-агентов
— Математика для разработки AI-моделей
— ML для старта в Data Science
— Математика для Data Science
— Специалист по ИИ
— Алгоритмы и структуры данных
— Программирование на Python
— Основы IT для непрограммистов
— Архитектуры и шаблоны проектирования
Забрать по старой цене
⚠️ Повышение уже 19 января
🥱1
Паттерн стратегия подходит, когда один и тот же сценарий можно выполнить несколькими способами, и выбор зависит от условий. Вместо большого
switch внутри сервиса разные варианты выносятся в отдельные классы с общим интерфейсом, а контекст просто делегирует работу выбранной реализации.Типовая схема такая. Есть интерфейс
Strategy, есть несколько конкретных стратегий, и есть контекст, который держит ссылку на стратегию и вызывает ее метод, не зная деталей реализации. Это снижает связность и позволяет добавлять новые варианты без переписывания старого кода.Мини пример на C#:
public interface IDiscountStrategy
{
decimal Apply(decimal total);
}
public sealed class RegularDiscount : IDiscountStrategy
{
public decimal Apply(decimal total) => total;
}
public sealed class VipDiscount : IDiscountStrategy
{
public decimal Apply(decimal total) => total * 0.9m;
}
public sealed class Checkout
{
private readonly IDiscountStrategy _discount;
public Checkout(IDiscountStrategy discount) => _discount = discount;
public decimal TotalWithDiscount(decimal total) => _discount.Apply(total);
}
Если стратегия выбирается по условиям, условие должно выбирать объект, а не ветку кода:
var checkout = serviceProvider.GetRequiredService<Checkout>();
var total = checkout.TotalWithDiscount(100m);
Выбор стратегии лучше делать на границе приложения, например в фабрике или при конфигурации через DI, потому что прямое создание зависимостей внутри сервиса жестко привязывает код к конкретной реализации.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3